forked from Upstream/mmocore
Skill Trees in MMOCore
This commit is contained in:
parent
a2630824c9
commit
fece7347ef
@ -78,7 +78,6 @@ public class MMOCore extends LuminePlugin {
|
||||
public final LootChestManager lootChests = new LootChestManager();
|
||||
public final MMOLoadManager loadManager = new MMOLoadManager();
|
||||
public final RestrictionManager restrictionManager = new RestrictionManager();
|
||||
@Deprecated
|
||||
public final SkillTreeManager skillTreeManager = new SkillTreeManager();
|
||||
|
||||
// Profession managers
|
||||
@ -242,7 +241,6 @@ public class MMOCore extends LuminePlugin {
|
||||
}
|
||||
|
||||
|
||||
|
||||
// Load party module
|
||||
try {
|
||||
String partyPluginName = UtilityMethods.enumName(getConfig().getString("party-plugin"));
|
||||
@ -330,7 +328,8 @@ public class MMOCore extends LuminePlugin {
|
||||
commandMap.register("mmocore", new PartyCommand(config.getConfigurationSection("party")));
|
||||
if (config.contains("guild"))
|
||||
commandMap.register("mmocore", new GuildCommand(config.getConfigurationSection("guild")));
|
||||
|
||||
if (config.contains("skill-tree"))
|
||||
commandMap.register("mmocore", new SkillTreeCommand(config.getConfigurationSection("skill-tree")));
|
||||
if (hasEconomy() && economy.isValid()) {
|
||||
if (config.contains("withdraw"))
|
||||
commandMap.register("mmocore", new WithdrawCommand(config.getConfigurationSection("withdraw")));
|
||||
@ -391,7 +390,7 @@ public class MMOCore extends LuminePlugin {
|
||||
* Called either when the server starts when initializing the manager for
|
||||
* the first time, or when issuing a plugin reload; in that case, stuff
|
||||
* like listeners must all be cleared before.
|
||||
*
|
||||
* <p>
|
||||
* Also see {@link MMOCoreManager}
|
||||
*
|
||||
* @param clearBefore True when issuing a plugin reload
|
||||
@ -427,6 +426,7 @@ public class MMOCore extends LuminePlugin {
|
||||
requestManager.initialize(clearBefore);
|
||||
soundManager.initialize(clearBefore);
|
||||
configItems.initialize(clearBefore);
|
||||
skillTreeManager.initialize(clearBefore);
|
||||
|
||||
if (getConfig().isConfigurationSection("action-bar"))
|
||||
actionBarManager.reload(getConfig().getConfigurationSection("action-bar"));
|
||||
|
@ -1,6 +1,5 @@
|
||||
package net.Indyuce.mmocore.api.player;
|
||||
|
||||
import io.lumine.mythic.lib.MythicLib;
|
||||
import io.lumine.mythic.lib.api.player.MMOPlayerData;
|
||||
import io.lumine.mythic.lib.player.TemporaryPlayerData;
|
||||
import io.lumine.mythic.lib.player.cooldown.CooldownMap;
|
||||
@ -8,7 +7,9 @@ import net.Indyuce.mmocore.MMOCore;
|
||||
import net.Indyuce.mmocore.api.ConfigMessage;
|
||||
import net.Indyuce.mmocore.api.SoundEvent;
|
||||
import net.Indyuce.mmocore.player.Unlockable;
|
||||
import net.Indyuce.mmocore.waypoint.CostType;
|
||||
import net.Indyuce.mmocore.tree.NodeState;
|
||||
import net.Indyuce.mmocore.tree.SkillTreeNode;
|
||||
import net.Indyuce.mmocore.tree.skilltree.SkillTree;
|
||||
import net.Indyuce.mmocore.waypoint.Waypoint;
|
||||
import net.Indyuce.mmocore.api.event.PlayerExperienceGainEvent;
|
||||
import net.Indyuce.mmocore.api.event.PlayerLevelUpEvent;
|
||||
@ -49,10 +50,12 @@ import org.bukkit.potion.PotionEffect;
|
||||
import org.bukkit.potion.PotionEffectType;
|
||||
import org.bukkit.scheduler.BukkitRunnable;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.w3c.dom.Node;
|
||||
|
||||
import javax.annotation.Nullable;
|
||||
import java.util.*;
|
||||
import java.util.logging.Level;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
|
||||
public class PlayerData extends OfflinePlayerData implements Closable, ExperienceTableClaimer {
|
||||
@ -113,6 +116,10 @@ public class PlayerData extends OfflinePlayerData implements Closable, Experienc
|
||||
*/
|
||||
private boolean fullyLoaded = false;
|
||||
|
||||
|
||||
//Value of the last skill tree the player was viewing
|
||||
private SkillTree cachedSkillTree = null;
|
||||
private final HashMap<SkillTreeNode, NodeState> nodeStates= new HashMap<>();
|
||||
/**
|
||||
* If the player data was loaded using temporary data.
|
||||
* See {@link TemporaryPlayerData} for more info
|
||||
@ -168,6 +175,14 @@ public class PlayerData extends OfflinePlayerData implements Closable, Experienc
|
||||
}
|
||||
}
|
||||
|
||||
public NodeState getNodeState(SkillTreeNode node) {
|
||||
return nodeStates.get(node);
|
||||
}
|
||||
|
||||
public void setNodeState(SkillTreeNode node,NodeState nodeState) {
|
||||
nodeStates.put(node,nodeState);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void close() {
|
||||
|
||||
@ -226,6 +241,17 @@ public class PlayerData extends OfflinePlayerData implements Closable, Experienc
|
||||
return Math.max(1, level);
|
||||
}
|
||||
|
||||
public void setCachedSkillTree(SkillTree cachedSkillTree) {
|
||||
this.cachedSkillTree = cachedSkillTree;
|
||||
}
|
||||
|
||||
public SkillTree getCachedSkillTree() {
|
||||
|
||||
if (cachedSkillTree == null)
|
||||
return MMOCore.plugin.skillTreeManager.getAll().stream().collect(Collectors.toList()).get(0);
|
||||
return cachedSkillTree;
|
||||
}
|
||||
|
||||
@Nullable
|
||||
public AbstractParty getParty() {
|
||||
return MMOCore.plugin.partyModule.getParty(this);
|
||||
|
@ -29,6 +29,7 @@ import java.io.ByteArrayOutputStream;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.List;
|
||||
import java.util.Locale;
|
||||
|
||||
public class MMOCoreUtils {
|
||||
public static boolean pluginItem(ItemStack item) {
|
||||
@ -54,6 +55,10 @@ public class MMOCoreUtils {
|
||||
return builder.toString();
|
||||
}
|
||||
|
||||
public static String toEnumName(String str) {
|
||||
return str.replace("-", "_").toUpperCase();
|
||||
}
|
||||
|
||||
/**
|
||||
* Displays an in game indicator using a hologram. This uses
|
||||
* LumineUtils hologramFactory to summon holograms
|
||||
|
@ -0,0 +1,38 @@
|
||||
package net.Indyuce.mmocore.command;
|
||||
|
||||
import net.Indyuce.mmocore.api.event.MMOCommandEvent;
|
||||
import net.Indyuce.mmocore.api.player.PlayerData;
|
||||
import net.Indyuce.mmocore.manager.InventoryManager;
|
||||
import org.bukkit.Bukkit;
|
||||
import org.bukkit.command.Command;
|
||||
import org.bukkit.command.CommandExecutor;
|
||||
import org.bukkit.command.CommandSender;
|
||||
import org.bukkit.command.defaults.BukkitCommand;
|
||||
import org.bukkit.configuration.ConfigurationSection;
|
||||
import org.bukkit.entity.Player;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
|
||||
import java.util.Optional;
|
||||
|
||||
public class SkillTreeCommand extends BukkitCommand {
|
||||
public SkillTreeCommand(ConfigurationSection config) {
|
||||
super(config.getString("main"));
|
||||
|
||||
setAliases(config.getStringList("aliases"));
|
||||
setDescription("Opens the skills menu.");
|
||||
}
|
||||
@Override
|
||||
public boolean execute(@NotNull CommandSender sender, String s, String[] args) {
|
||||
if (!(sender instanceof Player))
|
||||
return false;
|
||||
PlayerData data = PlayerData.get((Player) sender);
|
||||
MMOCommandEvent event = new MMOCommandEvent(data, "skills");
|
||||
Bukkit.getServer().getPluginManager().callEvent(event);
|
||||
if (event.isCancelled())
|
||||
return true;
|
||||
InventoryManager.TREE_VIEW.newInventory(data).open();
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
}
|
345
src/main/java/net/Indyuce/mmocore/gui/SkillTreeViewer.java
Normal file
345
src/main/java/net/Indyuce/mmocore/gui/SkillTreeViewer.java
Normal file
@ -0,0 +1,345 @@
|
||||
package net.Indyuce.mmocore.gui;
|
||||
|
||||
import io.lumine.mythic.lib.player.modifier.PlayerModifier;
|
||||
import net.Indyuce.mmocore.MMOCore;
|
||||
import net.Indyuce.mmocore.api.player.PlayerData;
|
||||
import net.Indyuce.mmocore.gui.api.EditableInventory;
|
||||
import net.Indyuce.mmocore.gui.api.GeneratedInventory;
|
||||
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.tree.IntegerCoordinates;
|
||||
import net.Indyuce.mmocore.tree.NodeState;
|
||||
import net.Indyuce.mmocore.tree.skilltree.SkillTree;
|
||||
import net.Indyuce.mmocore.tree.SkillTreeNode;
|
||||
import org.apache.commons.lang.Validate;
|
||||
import org.bukkit.Material;
|
||||
import org.bukkit.NamespacedKey;
|
||||
import org.bukkit.configuration.ConfigurationSection;
|
||||
import org.bukkit.event.inventory.InventoryAction;
|
||||
import org.bukkit.event.inventory.InventoryClickEvent;
|
||||
import org.bukkit.inventory.ItemStack;
|
||||
import org.bukkit.inventory.meta.ItemMeta;
|
||||
import org.bukkit.persistence.PersistentDataContainer;
|
||||
import org.bukkit.persistence.PersistentDataType;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
public class SkillTreeViewer extends EditableInventory {
|
||||
|
||||
|
||||
public SkillTreeViewer() {
|
||||
super("skill-tree");
|
||||
}
|
||||
|
||||
@Override
|
||||
public InventoryItem load(String function, ConfigurationSection config) {
|
||||
if (function.equals("skill-tree")) {
|
||||
return new SkillTreeItem(config);
|
||||
}
|
||||
if (function.equals("skill-tree-node"))
|
||||
return new SkillTreeNodeItem(config);
|
||||
if (function.equals("next-tree-list-page")) {
|
||||
return new NextTreeListPageItem(config);
|
||||
}
|
||||
if (function.equals("previous-tree-list-page")) {
|
||||
return new PreviousTreeListPageItem(config);
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
|
||||
public SkillTreeInventory newInventory(PlayerData playerData) {
|
||||
return new SkillTreeInventory(playerData, this);
|
||||
}
|
||||
|
||||
|
||||
public class SkillTreeItem extends InventoryItem<SkillTreeInventory> {
|
||||
|
||||
public SkillTreeItem(ConfigurationSection config) {
|
||||
super(config);
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public ItemStack display(SkillTreeInventory inv, int n) {
|
||||
int index = 4 * inv.treeListPage + n;
|
||||
SkillTree skillTree = MMOCore.plugin.skillTreeManager.get(index);
|
||||
//We display with the material corresponding to the skillTree
|
||||
ItemStack item = super.display(inv, n, skillTree.getGuiMaterial());
|
||||
ItemMeta meta = item.getItemMeta();
|
||||
PersistentDataContainer container = meta.getPersistentDataContainer();
|
||||
container.set(new NamespacedKey(MMOCore.plugin, "skill-tree-id"), PersistentDataType.STRING, skillTree.getId());
|
||||
item.setItemMeta(meta);
|
||||
return item;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Placeholders getPlaceholders(SkillTreeInventory inv, int n) {
|
||||
int index = 4 * inv.treeListPage + n;
|
||||
SkillTree skillTree = MMOCore.plugin.skillTreeManager.get(index);
|
||||
Placeholders holders = new Placeholders();
|
||||
holders.register("name", skillTree.getName());
|
||||
holders.register("id", skillTree.getId());
|
||||
return holders;
|
||||
}
|
||||
}
|
||||
|
||||
public class NextTreeListPageItem extends SimplePlaceholderItem<SkillTreeInventory> {
|
||||
|
||||
public NextTreeListPageItem(ConfigurationSection config) {
|
||||
super(config);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean canDisplay(SkillTreeInventory inv) {
|
||||
return inv.getTreeListPage() < inv.getMaxTreeListPage();
|
||||
}
|
||||
}
|
||||
|
||||
public class PreviousTreeListPageItem extends SimplePlaceholderItem<SkillTreeInventory> {
|
||||
|
||||
public PreviousTreeListPageItem(ConfigurationSection config) {
|
||||
super(config);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean canDisplay(SkillTreeInventory inv) {
|
||||
return inv.getTreeListPage() > 0;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
public class SkillTreeNodeItem extends InventoryItem<SkillTreeInventory> {
|
||||
private final LockedSkillNodeItem lockedSkillNode;
|
||||
private final UnlockedSkillNodeItem unlockedSkillNode;
|
||||
private final PathTreeNodeItem pathTreeNode;
|
||||
|
||||
|
||||
public SkillTreeNodeItem(ConfigurationSection config) {
|
||||
super(config);
|
||||
Validate.isTrue(config.contains("locked-skill-node"));
|
||||
Validate.isTrue(config.contains("unlocked-skill-node"));
|
||||
Validate.isTrue(config.contains("path-tree-node"));
|
||||
lockedSkillNode = new LockedSkillNodeItem(config.getConfigurationSection("locked-skill-node"));
|
||||
unlockedSkillNode = new UnlockedSkillNodeItem(config.getConfigurationSection("unlocked-skill-node"));
|
||||
pathTreeNode = new PathTreeNodeItem(config.getConfigurationSection("path-tree-node"));
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public ItemStack display(SkillTreeInventory inv, int n) {
|
||||
int slot = getSlots().get(n);
|
||||
int deltaX = (slot - inv.getMinSlot()) % 9;
|
||||
int deltaY = (slot - inv.getMinSlot()) / 9;
|
||||
IntegerCoordinates coordinates = new IntegerCoordinates(inv.getX() + deltaX, inv.getY() + deltaY);
|
||||
ItemStack item=null;
|
||||
if (inv.getSkillTree().isNode(coordinates)) {
|
||||
SkillTreeNode node = inv.getSkillTree().getNode(coordinates);
|
||||
if (inv.getPlayerData().getNodeState(node).equals(NodeState.UNLOCKED))
|
||||
item = unlockedSkillNode.display(inv, n, coordinates);
|
||||
else if (inv.getPlayerData().getNodeState(node).equals(NodeState.LOCKED))
|
||||
item = lockedSkillNode.display(inv, n, coordinates);
|
||||
|
||||
} //We check if is a path only if the skillTree is an automatic Skill Tree
|
||||
else if (inv.getSkillTree().isPath(coordinates))
|
||||
item = pathTreeNode.display(inv, n);
|
||||
else
|
||||
//If it is none of the above we just display air
|
||||
return new ItemStack(Material.AIR);
|
||||
|
||||
//We save the coordinates of the node
|
||||
ItemMeta meta = item.getItemMeta();
|
||||
PersistentDataContainer container = meta.getPersistentDataContainer();
|
||||
container.set(new NamespacedKey(MMOCore.plugin, "coordinates.x"), PersistentDataType.INTEGER, coordinates.getX());
|
||||
container.set(new NamespacedKey(MMOCore.plugin, "coordinates.y"), PersistentDataType.INTEGER, coordinates.getY());
|
||||
item.setItemMeta(meta);
|
||||
return item;
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public Placeholders getPlaceholders(SkillTreeInventory inv, int n) {
|
||||
return new Placeholders();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
public class LockedSkillNodeItem extends InventoryItem<SkillTreeInventory> {
|
||||
|
||||
public LockedSkillNodeItem(ConfigurationSection config) {
|
||||
super(config);
|
||||
}
|
||||
|
||||
public ItemStack display(SkillTreeInventory inv, int n, IntegerCoordinates coordinates) {
|
||||
return super.display(inv, n);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Placeholders getPlaceholders(SkillTreeInventory inv, int n) {
|
||||
int slot = getSlots().get(n);
|
||||
int deltaX = (slot - inv.getMinSlot()) % 9;
|
||||
int deltaY = (slot - inv.getMinSlot()) / 9;
|
||||
IntegerCoordinates coordinates = new IntegerCoordinates(inv.getX() + deltaX, inv.getY() + deltaY);
|
||||
SkillTreeNode treeNode = inv.getSkillTree().getNode(coordinates);
|
||||
Placeholders holders = new Placeholders();
|
||||
holders.register("name", treeNode.getName());
|
||||
holders.register("node-state", inv.getPlayerData().getNodeState(treeNode));
|
||||
//Display what nodes this node unlocks
|
||||
String str = "";
|
||||
for (SkillTreeNode node : treeNode.getChildren())
|
||||
str += node.getName() + ",";
|
||||
//We remove the last comma
|
||||
str = str.substring(0, str.length() - 1);
|
||||
holders.register("unlocks", str);
|
||||
//Display all the modifiers this node gives
|
||||
str = "";
|
||||
for (PlayerModifier playerModifier : treeNode.getModifiers()) {
|
||||
//TODO
|
||||
str += "\n" + playerModifier.getKey();
|
||||
}
|
||||
|
||||
holders.register("modifiers", str);
|
||||
|
||||
return holders;
|
||||
}
|
||||
}
|
||||
|
||||
public class UnlockedSkillNodeItem extends InventoryItem<SkillTreeInventory> {
|
||||
public UnlockedSkillNodeItem(ConfigurationSection config) {
|
||||
super(config);
|
||||
}
|
||||
|
||||
|
||||
public ItemStack display(SkillTreeInventory inv, int n, IntegerCoordinates coordinates) {
|
||||
return super.display(inv, n);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Placeholders getPlaceholders(SkillTreeInventory inv, int n) {
|
||||
int slot = getSlots().get(n);
|
||||
int deltaX = (slot - inv.getMinSlot()) % 9;
|
||||
int deltaY = (slot - inv.getMinSlot()) / 9;
|
||||
IntegerCoordinates coordinates = new IntegerCoordinates(inv.getX() + deltaX, inv.getY() + deltaY);
|
||||
SkillTreeNode treeNode = inv.getSkillTree().getNode(coordinates);
|
||||
Placeholders holders = new Placeholders();
|
||||
holders.register("name", treeNode.getName());
|
||||
holders.register("node-state", inv.getPlayerData().getNodeState(treeNode));
|
||||
String str = "";
|
||||
for (SkillTreeNode node : treeNode.getChildren())
|
||||
str += node.getName() + ",";
|
||||
//We remove the last comma
|
||||
str = str.substring(0, str.length() - 1);
|
||||
holders.register("unlocks", str);
|
||||
str = "";
|
||||
for (PlayerModifier playerModifier : treeNode.getModifiers()) {
|
||||
//TODO
|
||||
str += "\n" + playerModifier.getKey();
|
||||
}
|
||||
|
||||
holders.register("modifiers", str);
|
||||
return holders;
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
||||
public class PathTreeNodeItem extends SimplePlaceholderItem<SkillTreeInventory> {
|
||||
public PathTreeNodeItem(ConfigurationSection config) {
|
||||
super(config);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
public class SkillTreeInventory extends GeneratedInventory {
|
||||
private int x, y;
|
||||
//width and height correspond to the the size of the 'board' representing the skill tree
|
||||
private int minSlot, middleSlot, maxSlot;
|
||||
private final int width, height;
|
||||
private int treeListPage;
|
||||
private final int maxTreeListPage;
|
||||
private final SkillTree skillTree;
|
||||
|
||||
|
||||
public SkillTreeInventory(PlayerData playerData, EditableInventory editable) {
|
||||
super(playerData, editable);
|
||||
skillTree = playerData.getCachedSkillTree();
|
||||
|
||||
maxTreeListPage = MMOCore.plugin.skillTreeManager.getAll().size() / 4;
|
||||
|
||||
//We get the width and height of the GUI(corresponding to the slots given)
|
||||
List<Integer> slots = getByFunction("skill-tree-node").getSlots();
|
||||
minSlot = 64;
|
||||
maxSlot = 0;
|
||||
for (int slot : slots) {
|
||||
if (slot < minSlot)
|
||||
minSlot = slot;
|
||||
if (slot > maxSlot)
|
||||
maxSlot = slot;
|
||||
}
|
||||
width = (maxSlot - minSlot) % 9;
|
||||
height = (maxSlot - minSlot) / 9;
|
||||
middleSlot = minSlot + width / 2 + 9 * (height / 2);
|
||||
}
|
||||
|
||||
public int getX() {
|
||||
return x;
|
||||
}
|
||||
|
||||
public int getY() {
|
||||
return y;
|
||||
}
|
||||
|
||||
public int getTreeListPage() {
|
||||
return treeListPage;
|
||||
}
|
||||
|
||||
public int getMaxTreeListPage() {
|
||||
return maxTreeListPage;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String calculateName() {
|
||||
return getEditable().getName().replace("{skill-tree-name}", skillTree.getName()).replace("{skill-tree-id}", skillTree.getId());
|
||||
}
|
||||
|
||||
public SkillTree getSkillTree() {
|
||||
return skillTree;
|
||||
}
|
||||
|
||||
public int getMinSlot() {
|
||||
return minSlot;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void whenClicked(InventoryClickEvent event, InventoryItem item) {
|
||||
if (event.getAction().equals(InventoryAction.MOVE_TO_OTHER_INVENTORY)) {
|
||||
int offset = event.getSlot() - middleSlot;
|
||||
x += offset % 9;
|
||||
y += offset / 9;
|
||||
open();
|
||||
return;
|
||||
}
|
||||
if (item.getFunction().equals("next-tree-list-page")) {
|
||||
treeListPage++;
|
||||
open();
|
||||
}
|
||||
|
||||
if (item.getFunction().equals("previous-tree-list-page")) {
|
||||
treeListPage--;
|
||||
open();
|
||||
}
|
||||
|
||||
if (item.getFunction().equals("skill-tree")) {
|
||||
String id = event.getCurrentItem().getItemMeta().getPersistentDataContainer().get(
|
||||
new NamespacedKey(MMOCore.plugin, "skill-tree-id"), PersistentDataType.STRING);
|
||||
playerData.setCachedSkillTree(MMOCore.plugin.skillTreeManager.get(id));
|
||||
newInventory(playerData).open();
|
||||
}
|
||||
if (item.getFunction().equals("skill-tree-node")) {
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
@ -134,9 +134,13 @@ public abstract class InventoryItem<T extends GeneratedInventory> {
|
||||
}
|
||||
|
||||
public ItemStack display(T inv,int n) {
|
||||
return display(inv,n,null);
|
||||
}
|
||||
|
||||
public ItemStack display(T inv, int n,Material specificMaterial) {
|
||||
|
||||
Placeholders placeholders = getPlaceholders(inv, n);
|
||||
ItemStack item = new ItemStack(material);
|
||||
ItemStack item = new ItemStack(specificMaterial==null?material:specificMaterial);
|
||||
ItemMeta meta = item.getItemMeta();
|
||||
|
||||
if (texture != null && meta instanceof SkullMeta)
|
||||
|
@ -6,15 +6,7 @@ import java.util.logging.Level;
|
||||
|
||||
import net.Indyuce.mmocore.MMOCore;
|
||||
import net.Indyuce.mmocore.api.ConfigFile;
|
||||
import net.Indyuce.mmocore.gui.AttributeView;
|
||||
import net.Indyuce.mmocore.gui.ClassConfirmation;
|
||||
import net.Indyuce.mmocore.gui.ClassSelect;
|
||||
import net.Indyuce.mmocore.gui.PlayerStats;
|
||||
import net.Indyuce.mmocore.gui.QuestViewer;
|
||||
import net.Indyuce.mmocore.gui.SkillList;
|
||||
import net.Indyuce.mmocore.gui.SubclassConfirmation;
|
||||
import net.Indyuce.mmocore.gui.SubclassSelect;
|
||||
import net.Indyuce.mmocore.gui.WaypointViewer;
|
||||
import net.Indyuce.mmocore.gui.*;
|
||||
import net.Indyuce.mmocore.gui.api.EditableInventory;
|
||||
import net.Indyuce.mmocore.gui.social.friend.EditableFriendList;
|
||||
import net.Indyuce.mmocore.gui.social.friend.EditableFriendRemoval;
|
||||
@ -39,7 +31,7 @@ public class InventoryManager {
|
||||
public static final EditableGuildCreation GUILD_CREATION = new EditableGuildCreation();
|
||||
public static final QuestViewer QUEST_LIST = new QuestViewer();
|
||||
public static final AttributeView ATTRIBUTE_VIEW = new AttributeView();
|
||||
|
||||
public static final SkillTreeViewer TREE_VIEW = new SkillTreeViewer();
|
||||
public static final List<EditableInventory> list = Arrays.asList(PLAYER_STATS, ATTRIBUTE_VIEW, SKILL_LIST, CLASS_SELECT, SUBCLASS_SELECT, SUBCLASS_CONFIRM, QUEST_LIST, WAYPOINTS, CLASS_CONFIRM, FRIEND_LIST, FRIEND_REMOVAL, PARTY_VIEW, PARTY_CREATION, GUILD_VIEW, GUILD_CREATION);
|
||||
|
||||
public static void load() {
|
||||
|
@ -1,21 +1,63 @@
|
||||
package net.Indyuce.mmocore.manager;
|
||||
|
||||
import net.Indyuce.mmocore.MMOCore;
|
||||
import net.Indyuce.mmocore.gui.SkillTreeViewer;
|
||||
import net.Indyuce.mmocore.manager.registry.MMOCoreRegister;
|
||||
import net.Indyuce.mmocore.tree.SkillTree;
|
||||
import net.Indyuce.mmocore.tree.SkillTreeNode;
|
||||
import net.Indyuce.mmocore.tree.skilltree.SkillTree;
|
||||
import org.bukkit.configuration.file.YamlConfiguration;
|
||||
|
||||
import java.io.File;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.List;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
|
||||
@Deprecated
|
||||
public class SkillTreeManager extends MMOCoreRegister<SkillTree> {
|
||||
private final ArrayList<SkillTreeNode> skillTreeNodes = new ArrayList<>();
|
||||
|
||||
@Override
|
||||
public void register(SkillTree tree){
|
||||
super.register(tree);
|
||||
tree.getNodes().forEach((node)->skillTreeNodes.add(node));
|
||||
}
|
||||
|
||||
|
||||
public ArrayList<SkillTreeNode> getAllNodes() {
|
||||
return skillTreeNodes;
|
||||
}
|
||||
|
||||
public SkillTree get(int index) {
|
||||
return registered.values().stream().collect(Collectors.toList()).get(index);
|
||||
}
|
||||
|
||||
@Override
|
||||
|
||||
public String getRegisteredObjectName() {
|
||||
return "skill tree";
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public void initialize(boolean clearBefore) {
|
||||
if (clearBefore)
|
||||
registered.clear();
|
||||
File file = new File(MMOCore.plugin.getDataFolder() + "/skillTree");
|
||||
if (!file.exists())
|
||||
file.mkdirs();
|
||||
load(file);
|
||||
}
|
||||
|
||||
// TODO
|
||||
|
||||
public void load(File file) {
|
||||
if (file.isDirectory()) {
|
||||
List<File> fileList = Arrays.asList(file.listFiles()).stream().sorted().collect(Collectors.toList());
|
||||
for (File child : fileList) {
|
||||
load(child);
|
||||
}
|
||||
} else {
|
||||
register(SkillTree.loadSkillTree(YamlConfiguration.loadConfiguration(file)));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -10,6 +10,8 @@ import net.Indyuce.mmocore.api.player.stats.StatType;
|
||||
import net.Indyuce.mmocore.guild.provided.Guild;
|
||||
import net.Indyuce.mmocore.manager.data.DataProvider;
|
||||
import net.Indyuce.mmocore.manager.data.PlayerDataManager;
|
||||
import net.Indyuce.mmocore.tree.NodeState;
|
||||
import net.Indyuce.mmocore.tree.SkillTreeNode;
|
||||
import org.apache.commons.lang.Validate;
|
||||
import org.bukkit.configuration.file.FileConfiguration;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
@ -67,11 +69,16 @@ public class YAMLPlayerDataManager extends PlayerDataManager {
|
||||
for (String id : config.getStringList("bound-skills"))
|
||||
if (data.getProfess().hasSkill(id))
|
||||
data.getBoundSkills().add(data.getProfess().getSkill(id));
|
||||
|
||||
if (config.contains("times-claimed"))
|
||||
for (String key : config.getConfigurationSection("times-claimed").getKeys(true))
|
||||
data.getItemClaims().put(key, config.getInt("times-claimed." + key));
|
||||
|
||||
for (SkillTreeNode node : MMOCore.plugin.skillTreeManager.getAllNodes()) {
|
||||
String str = config.getString("skill-tree-nodes." + node.getTree().getId() + "." + node.getId());
|
||||
if (str == null)
|
||||
data.setNodeState(node, NodeState.LOCKED);
|
||||
else
|
||||
data.setNodeState(node,NodeState.valueOf(str));
|
||||
}
|
||||
// Load class slots, use try so the player can log in.
|
||||
if (config.contains("class-info"))
|
||||
for (String key : config.getConfigurationSection("class-info").getKeys(false))
|
||||
@ -108,6 +115,10 @@ public class YAMLPlayerDataManager extends PlayerDataManager {
|
||||
data.mapSkillLevels().forEach((key1, value) -> config.set("skill." + key1, value));
|
||||
data.getItemClaims().forEach((key, times) -> config.set("times-claimed." + key, times));
|
||||
|
||||
//Save the node states for the player
|
||||
for (SkillTreeNode node : MMOCore.plugin.skillTreeManager.getAllNodes()) {
|
||||
config.set("skill-tree-nodes." + node.getTree().getId() + "." + node.getId(),data.getNodeState(node));
|
||||
}
|
||||
List<String> boundSkills = new ArrayList<>();
|
||||
data.getBoundSkills().forEach(skill -> boundSkills.add(skill.getSkill().getHandler().getId()));
|
||||
config.set("bound-skills", boundSkills);
|
||||
|
@ -6,8 +6,9 @@ import net.Indyuce.mmocore.quest.compat.QuestModule;
|
||||
import org.bukkit.entity.Player;
|
||||
|
||||
public class MMOCoreQuestModule implements QuestModule {
|
||||
|
||||
@Override
|
||||
public AbstractQuest getQuestOrThrow(String id) {
|
||||
public AbstractQuest getQuest(String id) {
|
||||
Quest quest=MMOCore.plugin.questManager.get(id);
|
||||
if(quest==null)
|
||||
return null;
|
||||
|
@ -8,11 +8,13 @@ import fr.skytasul.quests.structure.Quest;
|
||||
import net.Indyuce.mmocore.quest.AbstractQuest;
|
||||
import org.bukkit.entity.Player;
|
||||
|
||||
import javax.annotation.Nonnull;
|
||||
|
||||
public class BeautyQuestModule implements QuestModule<BeautyQuestModule.BeautyQuestQuest> {
|
||||
|
||||
|
||||
@Override
|
||||
public BeautyQuestQuest getQuestOrThrow(String questId) {
|
||||
public BeautyQuestQuest getQuest(String questId) {
|
||||
Quest quest=QuestsAPI.getQuests().getQuest(Integer.parseInt(questId));
|
||||
return quest==null?null:new BeautyQuestQuest(quest);
|
||||
}
|
||||
@ -20,15 +22,15 @@ public class BeautyQuestModule implements QuestModule<BeautyQuestModule.BeautyQu
|
||||
@Override
|
||||
public boolean hasCompletedQuest(String questId, Player player) {
|
||||
PlayerAccount account=PlayersManager.getPlayerAccount(player);
|
||||
PlayerQuestDatas quest=account.getQuestDatas(QuestsAPI.getQuests().getQuest(Integer.parseInt(questId)));
|
||||
return quest.isFinished();
|
||||
Quest quest=QuestsAPI.getQuests().getQuest(Integer.parseInt(questId));
|
||||
PlayerQuestDatas questData=account.getQuestDatas(quest);
|
||||
return questData.isFinished();
|
||||
}
|
||||
|
||||
|
||||
|
||||
public class BeautyQuestQuest implements AbstractQuest {
|
||||
|
||||
Quest quest;
|
||||
private final Quest quest;
|
||||
|
||||
public BeautyQuestQuest(Quest quest) {
|
||||
this.quest = quest;
|
||||
|
@ -14,16 +14,17 @@ public class BlackVeinQuestsModule implements QuestModule<BlackVeinQuestsModule.
|
||||
|
||||
|
||||
@Override
|
||||
public BlackVeinQuestQuest getQuestOrThrow(String id) {
|
||||
Quests plugin = (Quests) Bukkit.getPluginManager().getPlugin("Quests");
|
||||
return plugin.getQuestById(id)==null?null:new BlackVeinQuestQuest(plugin.getQuestById(id));
|
||||
public BlackVeinQuestQuest getQuest(String id) {
|
||||
Quest quest=plugin.getQuestById(id);
|
||||
return quest==null?null:new BlackVeinQuestQuest(quest);
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public boolean hasCompletedQuest(String questId, Player player) {
|
||||
Quester quester = plugin.getQuester(player.getUniqueId());
|
||||
|
||||
if(quester==null)
|
||||
return false;
|
||||
for(Quest quest:quester.getCompletedQuests()) {
|
||||
if(quest.getId().equals(questId))
|
||||
return true;
|
||||
|
@ -14,14 +14,15 @@ import java.util.List;
|
||||
public class QuestCreatorModule implements QuestModule<QuestCreatorModule.QuestCreatorQuest>{
|
||||
|
||||
@Override
|
||||
public QuestCreatorQuest getQuestOrThrow(String id) {
|
||||
public QuestCreatorQuest getQuest(String id) {
|
||||
return new QuestCreatorQuest(id);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean hasCompletedQuest(String questId, Player player) {
|
||||
UserQC playerData=UserQC.cachedOrNull(player);
|
||||
Validate.notNull(playerData,"QuestCreator User hasn't been loaded!");
|
||||
if(playerData==null)
|
||||
return false;
|
||||
//Gets all the quests the player has succeeded at
|
||||
List<QuestHistoryElement> elements=playerData.getQuestHistory().getElements(questId, Arrays.asList(QuestEndType.SUCCESS),0);
|
||||
for(QuestHistoryElement el:elements) {
|
||||
|
@ -3,12 +3,15 @@ package net.Indyuce.mmocore.quest.compat;
|
||||
import net.Indyuce.mmocore.quest.AbstractQuest;
|
||||
import org.bukkit.entity.Player;
|
||||
|
||||
import javax.annotation.Nullable;
|
||||
|
||||
public interface QuestModule<T extends AbstractQuest> {
|
||||
|
||||
/**
|
||||
* @return Quest with given name
|
||||
*/
|
||||
public T getQuestOrThrow(String id);
|
||||
@Nullable
|
||||
public T getQuest(String id);
|
||||
|
||||
/**
|
||||
* @return If a specific player did a certain quest
|
||||
|
5
src/main/java/net/Indyuce/mmocore/tree/NodeState.java
Normal file
5
src/main/java/net/Indyuce/mmocore/tree/NodeState.java
Normal file
@ -0,0 +1,5 @@
|
||||
package net.Indyuce.mmocore.tree;
|
||||
|
||||
public enum NodeState {
|
||||
LOCKED,UNLOCKED;
|
||||
}
|
@ -1,79 +0,0 @@
|
||||
package net.Indyuce.mmocore.tree;
|
||||
|
||||
import net.Indyuce.mmocore.MMOCore;
|
||||
import net.Indyuce.mmocore.manager.registry.RegisterObject;
|
||||
import org.apache.commons.lang.Validate;
|
||||
import org.bukkit.configuration.ConfigurationSection;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
|
||||
import java.util.Collection;
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
import java.util.Objects;
|
||||
import java.util.logging.Level;
|
||||
|
||||
/**
|
||||
* A passive skill tree that features nodes, or passive skills.
|
||||
* <p>
|
||||
* The player can explore the passive skill tree using the right GUI
|
||||
* and unlock nodes by spending passive skill points. Unlocking nodes
|
||||
* grant permanent player modifiers, including
|
||||
* - stats
|
||||
* - active or passive MythicLib skills
|
||||
* - active or passive MMOCore skills
|
||||
* - extra attribute pts
|
||||
* - particle or potion effects
|
||||
*
|
||||
* @author jules
|
||||
* @see {@link SkillTreeNode}
|
||||
*/
|
||||
public class SkillTree implements RegisterObject {
|
||||
private final String id, name;
|
||||
private final Map<IntegerCoordinates, SkillTreeNode> nodes = new HashMap<>();
|
||||
|
||||
public SkillTree(ConfigurationSection config) {
|
||||
this.id = config.getName();
|
||||
this.name = Objects.requireNonNull(config.getString("name"), "Could not find skill tree name");
|
||||
Validate.isTrue(config.isConfigurationSection("nodes"), "Could not find tree passive skills");
|
||||
for (String xKey : config.getConfigurationSection("nodes").getKeys(false))
|
||||
for (String yKey : config.getConfigurationSection("nodes." + xKey).getKeys(false))
|
||||
try {
|
||||
int x = Integer.parseInt(xKey), y = Integer.parseInt(yKey);
|
||||
SkillTreeNode node = new SkillTreeNode(this, x, y, config.getConfigurationSection("nodes." + xKey + "." + yKey));
|
||||
nodes.put(node.getCoordinates(), node);
|
||||
} catch (RuntimeException exception) {
|
||||
MMOCore.plugin.getLogger().log(Level.WARNING, "Could not load tree node '" + xKey + "." + yKey + "' for skill tree '" + id + "': " + exception.getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getId() {
|
||||
return id;
|
||||
}
|
||||
|
||||
public String getName() {
|
||||
return name;
|
||||
}
|
||||
|
||||
public Collection<SkillTreeNode> getNodes() {
|
||||
return nodes.values();
|
||||
}
|
||||
|
||||
@NotNull
|
||||
public SkillTreeNode getNode(IntegerCoordinates coords) {
|
||||
return Objects.requireNonNull(nodes.get(coords), "Could not find node in tree '" + id + "' with coordinates '" + coords.toString() + "'");
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean equals(Object o) {
|
||||
if (this == o) return true;
|
||||
if (o == null || getClass() != o.getClass()) return false;
|
||||
SkillTree skillTree = (SkillTree) o;
|
||||
return id.equals(skillTree.id);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int hashCode() {
|
||||
return Objects.hash(id);
|
||||
}
|
||||
}
|
@ -4,27 +4,53 @@ import io.lumine.mythic.lib.MythicLib;
|
||||
import io.lumine.mythic.lib.player.modifier.PlayerModifier;
|
||||
import io.lumine.mythic.lib.util.configobject.ConfigSectionObject;
|
||||
import net.Indyuce.mmocore.MMOCore;
|
||||
import net.Indyuce.mmocore.api.util.MMOCoreUtils;
|
||||
import net.Indyuce.mmocore.player.Unlockable;
|
||||
import net.Indyuce.mmocore.tree.skilltree.AutomaticSkillTree;
|
||||
import net.Indyuce.mmocore.tree.skilltree.SkillTree;
|
||||
import org.apache.commons.lang.Validate;
|
||||
import org.bukkit.configuration.ConfigurationSection;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
|
||||
import java.util.HashSet;
|
||||
import java.util.List;
|
||||
import java.util.Objects;
|
||||
import java.util.Set;
|
||||
import java.util.*;
|
||||
|
||||
public class SkillTreeNode implements Unlockable {
|
||||
private final SkillTree tree;
|
||||
private final String name;
|
||||
private final IntegerCoordinates coordinates;
|
||||
private final String name,id;
|
||||
private IntegerCoordinates coordinates;
|
||||
private final List<String> lore;
|
||||
private final Set<PlayerModifier> modifiers = new HashSet<>();
|
||||
private final ArrayList<SkillTreeNode> children = new ArrayList<>();
|
||||
private final ArrayList<SkillTreeNode> parents=new ArrayList<>();
|
||||
|
||||
|
||||
|
||||
public SkillTreeNode(SkillTree tree, ConfigurationSection config) {
|
||||
|
||||
Validate.notNull(config, "Config cannot be null");
|
||||
this.id=config.getName();
|
||||
this.tree = tree;
|
||||
name = Objects.requireNonNull(config.getString("name"), "Could not find node name");
|
||||
lore = config.getStringList("lore");
|
||||
Validate.isTrue(config.contains("node-type"),"Could not find the node type");
|
||||
|
||||
|
||||
//If coordinates are precised adn we are not wiht an automaticTreewe set them up
|
||||
if((!(tree instanceof AutomaticSkillTree))&&config.contains("coordinates.x")&&config.contains("coordinates.y")) {
|
||||
coordinates=new IntegerCoordinates(config.getInt("coordinates.x"),config.getInt("coordinates.y"));
|
||||
}
|
||||
for (String key : config.getConfigurationSection("modifiers").getKeys(false)) {
|
||||
PlayerModifier mod = MythicLib.plugin.getModifiers().loadPlayerModifier(new ConfigSectionObject(config.getConfigurationSection(key)));
|
||||
modifiers.add(mod);
|
||||
}
|
||||
}
|
||||
|
||||
public SkillTreeNode(SkillTree tree, int x, int y, ConfigurationSection config) {
|
||||
Validate.notNull(config, "Config cannot be null");
|
||||
this.id=config.getName();
|
||||
this.tree = tree;
|
||||
name = Objects.requireNonNull(config.getString("name"), "Could not find node name");
|
||||
Validate.isTrue(config.contains("node-type"),"Could not find the node type");
|
||||
coordinates = new IntegerCoordinates(x, y);
|
||||
lore = config.getStringList("lore");
|
||||
for (String key : config.getConfigurationSection("modifiers").getKeys(false)) {
|
||||
@ -33,6 +59,44 @@ public class SkillTreeNode implements Unlockable {
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
*
|
||||
*/
|
||||
protected void whenPostLoaded(@NotNull ConfigurationSection config) {
|
||||
|
||||
}
|
||||
|
||||
public SkillTree getTree() {
|
||||
return tree;
|
||||
}
|
||||
|
||||
//Used when postLoaded
|
||||
public void addParent(SkillTreeNode parent) {
|
||||
parents.add(parent);
|
||||
}
|
||||
|
||||
public void addChild(SkillTreeNode child) {children.add(child);}
|
||||
|
||||
public void setCoordinates(IntegerCoordinates coordinates) {
|
||||
this.coordinates = coordinates;
|
||||
}
|
||||
|
||||
|
||||
public ArrayList<SkillTreeNode> getParents() {
|
||||
return parents;
|
||||
}
|
||||
|
||||
public ArrayList<SkillTreeNode> getChildren() {
|
||||
return children;
|
||||
}
|
||||
|
||||
|
||||
|
||||
public String getId() {
|
||||
return id;
|
||||
}
|
||||
|
||||
public String getName() {
|
||||
return name;
|
||||
}
|
||||
@ -57,6 +121,8 @@ public class SkillTreeNode implements Unlockable {
|
||||
return "skill_tree:" + tree.getId() + "_" + coordinates.getX() + "_" + coordinates.getY();
|
||||
}
|
||||
|
||||
|
||||
|
||||
@Override
|
||||
public boolean equals(Object o) {
|
||||
if (this == o) return true;
|
||||
@ -91,4 +157,5 @@ public class SkillTreeNode implements Unlockable {
|
||||
String treeId = treeIdBuilder.toString();
|
||||
return MMOCore.plugin.skillTreeManager.get(treeId).getNode(coords);
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -0,0 +1,167 @@
|
||||
package net.Indyuce.mmocore.tree.skilltree;
|
||||
|
||||
import net.Indyuce.mmocore.tree.IntegerCoordinates;
|
||||
import net.Indyuce.mmocore.tree.SkillTreeNode;
|
||||
import org.apache.commons.lang.Validate;
|
||||
import org.bukkit.configuration.ConfigurationSection;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.HashMap;
|
||||
|
||||
public class AutomaticSkillTree extends SkillTree {
|
||||
private SkillTreeNode root;
|
||||
//Represents all the coordinates that will be displayed as a path (between 2 nodes of the tree)
|
||||
private final ArrayList<IntegerCoordinates> pathToParents = new ArrayList<>();
|
||||
|
||||
//Hash map to store the left and right branches of each node
|
||||
private final HashMap<SkillTreeNode, Branches> nodeBranches = new HashMap<>();
|
||||
|
||||
public AutomaticSkillTree(ConfigurationSection config) {
|
||||
super(config);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void whenPostLoaded(ConfigurationSection config) {
|
||||
|
||||
|
||||
//We setup the children and parents for each node.
|
||||
for (SkillTreeNode node : nodes.values()) {
|
||||
ConfigurationSection section = config.getConfigurationSection(node.getId());
|
||||
for (String child : section.getStringList("children")) {
|
||||
node.addChild(getNode(child));
|
||||
getNode(child).addParent(node);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
//We find the root of the tree wich is
|
||||
for (SkillTreeNode node : nodes.values()) {
|
||||
if (node.getParents().size() == 0) {
|
||||
Validate.isTrue(root == null, "Their can't be more than 1 root in the skillTree!");
|
||||
root = node;
|
||||
}
|
||||
}
|
||||
//We setup the width of all the nodes recursively
|
||||
setupTreeWidth(root);
|
||||
//We recursively setup all the coordinates of the tree nodes
|
||||
root.setCoordinates(new IntegerCoordinates(0, 0));
|
||||
setupCoordinates(root);
|
||||
|
||||
//We get and cache the values of minX,minY,maxX and maxY
|
||||
minX = nodeBranches.get(root).getLeftBranches();
|
||||
minY = 0;
|
||||
maxX = nodeBranches.get(root).getRightBranches();
|
||||
|
||||
for (SkillTreeNode node : nodes.values()) {
|
||||
if (node.getCoordinates().getY() > maxY)
|
||||
maxY = node.getCoordinates().getY();
|
||||
}
|
||||
|
||||
//Eventually we setup the coordinateNodesMap
|
||||
super.setupCoordinatesNodesMap();
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Recursive algorithm to automatically calculate the integercoordinates each node should have to have a good display.
|
||||
* It also fills the list pathToParents representing all the coordinates corresponding to a path between 2 nodes (for the GUI)
|
||||
*
|
||||
* @param node the root
|
||||
*/
|
||||
private void setupCoordinates(SkillTreeNode node) {
|
||||
int childrenSize = node.getChildren().size();
|
||||
int x = node.getCoordinates().getX();
|
||||
;
|
||||
int y = node.getCoordinates().getY();
|
||||
;
|
||||
int leftOffset = 0;
|
||||
int rightOffset = 0;
|
||||
for (int i = 0; i < childrenSize; i++) {
|
||||
SkillTreeNode child = node.getChildren().get(i);
|
||||
|
||||
if (childrenSize % 2 == 0 && i == 0) {
|
||||
child.setCoordinates(new IntegerCoordinates(x, y + 2));
|
||||
leftOffset += 2 + nodeBranches.get(child).getLeftBranches();
|
||||
rightOffset += 2 + nodeBranches.get(child).getRightBranches();
|
||||
} else if (i % 2 == 0) {
|
||||
child.setCoordinates(new IntegerCoordinates(x - leftOffset - 2 - nodeBranches.get(child).getWidth(), y + 2));
|
||||
leftOffset += 2 + nodeBranches.get(child).getWidth();
|
||||
} else {
|
||||
child.setCoordinates(new IntegerCoordinates(x + rightOffset + 2 + nodeBranches.get(child).getWidth(), y + 2));
|
||||
rightOffset += 2 + nodeBranches.get(child).getWidth();
|
||||
}
|
||||
|
||||
//We setup the path to parent variable (Used for the GUI)
|
||||
int childX = child.getCoordinates().getX();
|
||||
int childY = child.getCoordinates().getY();
|
||||
|
||||
int parentX = node.getParents().get(0).getCoordinates().getX();
|
||||
pathToParents.add(new IntegerCoordinates(childX, childY - 1));
|
||||
int offset = childX > parentX ? -1 : 1;
|
||||
while (childX != parentX) {
|
||||
pathToParents.add(new IntegerCoordinates(childX, childY - 2));
|
||||
childX += offset;
|
||||
}
|
||||
|
||||
|
||||
//We setup the coordinates for the associated child
|
||||
setupCoordinates(child);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
public Branches getBranches(SkillTreeNode node) {
|
||||
return nodeBranches.get(node);
|
||||
}
|
||||
|
||||
/**
|
||||
* Recursively sed to setup all the right and left branches of the node to later determine its coordinates for GUI display
|
||||
*/
|
||||
public void setupTreeWidth(SkillTreeNode node) {
|
||||
int childrenSize = node.getChildren().size();
|
||||
int leftBranches = 0;
|
||||
int rightBranches = 0;
|
||||
for (int i = 0; i < childrenSize; i++) {
|
||||
SkillTreeNode child = node.getChildren().get(i);
|
||||
setupTreeWidth(child);
|
||||
if (childrenSize % 2 == 0 && i == 0) {
|
||||
|
||||
leftBranches += nodeBranches.get(child).getLeftBranches();
|
||||
rightBranches += nodeBranches.get(child).getRightBranches();
|
||||
} else if (i % 2 == 0) {
|
||||
leftBranches += nodeBranches.get(child).getWidth() + 2;
|
||||
} else {
|
||||
rightBranches += nodeBranches.get(child).getWidth() + 2;
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isPath(IntegerCoordinates coordinates) {
|
||||
return pathToParents.contains(coordinates);
|
||||
}
|
||||
|
||||
|
||||
private class Branches {
|
||||
private final int leftBranches, rightBranches;
|
||||
|
||||
public Branches(int leftBranches, int rightBranches) {
|
||||
this.leftBranches = leftBranches;
|
||||
this.rightBranches = rightBranches;
|
||||
}
|
||||
|
||||
public int getLeftBranches() {
|
||||
return leftBranches;
|
||||
}
|
||||
|
||||
public int getRightBranches() {
|
||||
return rightBranches;
|
||||
}
|
||||
|
||||
public int getWidth() {
|
||||
return leftBranches + rightBranches;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -0,0 +1,21 @@
|
||||
package net.Indyuce.mmocore.tree.skilltree;
|
||||
|
||||
import net.Indyuce.mmocore.tree.IntegerCoordinates;
|
||||
import org.bukkit.configuration.ConfigurationSection;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
|
||||
public class CustomSkillTree extends SkillTree{
|
||||
public CustomSkillTree(ConfigurationSection config) {
|
||||
super(config);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void whenPostLoaded(@NotNull ConfigurationSection configurationSection) {
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isPath(IntegerCoordinates coordinates) {
|
||||
return false;
|
||||
}
|
||||
}
|
@ -0,0 +1,64 @@
|
||||
package net.Indyuce.mmocore.tree.skilltree;
|
||||
|
||||
import io.netty.handler.codec.http.cookie.CookieDecoder;
|
||||
import net.Indyuce.mmocore.tree.IntegerCoordinates;
|
||||
import net.Indyuce.mmocore.tree.SkillTreeNode;
|
||||
import org.apache.commons.lang.Validate;
|
||||
import org.bukkit.configuration.ConfigurationSection;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
|
||||
import java.util.Arrays;
|
||||
import java.util.List;
|
||||
|
||||
public class LinkedSkillTree extends SkillTree{
|
||||
|
||||
|
||||
public LinkedSkillTree(ConfigurationSection config) {
|
||||
super(config);
|
||||
|
||||
//We setup the coordinate map because coordinates are given in the yml for linked skill tree
|
||||
setupCoordinatesNodesMap();
|
||||
}
|
||||
|
||||
|
||||
|
||||
@Override
|
||||
protected void whenPostLoaded(@NotNull ConfigurationSection configurationSection) {
|
||||
|
||||
SkillTreeNode root=getNode(new IntegerCoordinates(0,0));
|
||||
Validate.notNull(root,"Their must be a node(the root of the tree) at the coordinates (0,0) ");
|
||||
//We setup all the children and parent relations between the nodes
|
||||
setupChildren(root);
|
||||
}
|
||||
|
||||
/**
|
||||
* There is no paths on a linked skill tree
|
||||
*/
|
||||
@Override
|
||||
public boolean isPath(IntegerCoordinates coordinates) {
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Recursive algorithm to setup the parents and children of each skillTreeNode
|
||||
*/
|
||||
public void setupChildren(SkillTreeNode node) {
|
||||
int x=node.getCoordinates().getX();
|
||||
int y=node.getCoordinates().getY();
|
||||
List<IntegerCoordinates> checkCoordinates= Arrays.asList(new IntegerCoordinates(x+1,y),
|
||||
new IntegerCoordinates(x-1,y),new IntegerCoordinates(x,y+1),new IntegerCoordinates(x,y-1));
|
||||
for(IntegerCoordinates coor:checkCoordinates) {
|
||||
//We add Parent and child only if the node exists and doesn't have a parent already
|
||||
|
||||
if(isNode(coor)) {
|
||||
SkillTreeNode child=getNode(coor);
|
||||
if(child.getParents().size()==0) {
|
||||
child.addParent(node);
|
||||
node.addChild(child);
|
||||
//We call recursively the algorithm
|
||||
setupChildren(child);
|
||||
}}
|
||||
|
||||
}
|
||||
}
|
||||
}
|
154
src/main/java/net/Indyuce/mmocore/tree/skilltree/SkillTree.java
Normal file
154
src/main/java/net/Indyuce/mmocore/tree/skilltree/SkillTree.java
Normal file
@ -0,0 +1,154 @@
|
||||
package net.Indyuce.mmocore.tree.skilltree;
|
||||
|
||||
import io.lumine.mythic.lib.api.util.PostLoadObject;
|
||||
import net.Indyuce.mmocore.api.util.MMOCoreUtils;
|
||||
import net.Indyuce.mmocore.manager.registry.RegisterObject;
|
||||
import net.Indyuce.mmocore.tree.IntegerCoordinates;
|
||||
import net.Indyuce.mmocore.tree.NodeState;
|
||||
import net.Indyuce.mmocore.tree.SkillTreeNode;
|
||||
import org.apache.commons.lang.Validate;
|
||||
import org.bukkit.Material;
|
||||
import org.bukkit.configuration.ConfigurationSection;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
import java.util.*;
|
||||
|
||||
/**
|
||||
* A passive skill tree that features nodes, or passive skills.
|
||||
* <p>
|
||||
* The player can explore the passive skill tree using the right GUI
|
||||
* and unlock nodes by spending passive skill points. Unlocking nodes
|
||||
* grant permanent player modifiers, including
|
||||
* - stats
|
||||
* - active or passive MythicLib skills
|
||||
* - active or passive MMOCore skills
|
||||
* - extra attribute pts
|
||||
* - particle or potion effects
|
||||
*
|
||||
* @author jules
|
||||
* @author Ka0rX
|
||||
* @see {@link SkillTreeNode}
|
||||
*/
|
||||
public abstract class SkillTree extends PostLoadObject implements RegisterObject {
|
||||
private final String id, name;
|
||||
private final Material guiMaterial;
|
||||
//2 different maps to get the nodes
|
||||
protected final Map<IntegerCoordinates, SkillTreeNode> coordinatesNodes = new HashMap<>();
|
||||
protected final Map<String, SkillTreeNode> nodes = new HashMap<>();
|
||||
//Caches the height of the skill tree
|
||||
protected int minX, minY, maxX, maxY;
|
||||
|
||||
public SkillTree(ConfigurationSection config) {
|
||||
super(config);
|
||||
this.id = Objects.requireNonNull(config.getString("id"), "Could not find skill tree id");
|
||||
this.name = Objects.requireNonNull(config.getString("name"), "Could not find skill tree name");
|
||||
this.guiMaterial = Material.valueOf(MMOCoreUtils.toEnumName(Objects.requireNonNull(config.getString("material"))));
|
||||
Validate.isTrue(config.isConfigurationSection("nodes"), "Could not find any nodes in the tree");
|
||||
for (String key : config.getConfigurationSection("nodes").getKeys(false)) {
|
||||
SkillTreeNode node = new SkillTreeNode(this, config.getConfigurationSection("nodes." + key));
|
||||
}
|
||||
}
|
||||
|
||||
public void setupCoordinatesNodesMap() {
|
||||
for (SkillTreeNode node : nodes.values()) {
|
||||
coordinatesNodes.put(node.getCoordinates(), node);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
protected abstract void whenPostLoaded(@NotNull ConfigurationSection configurationSection);
|
||||
|
||||
public int getMaxX() {
|
||||
return maxX;
|
||||
}
|
||||
|
||||
public int getMinX() {
|
||||
return minX;
|
||||
}
|
||||
|
||||
public int getMinY() {
|
||||
return minY;
|
||||
}
|
||||
|
||||
public int getMaxY() {
|
||||
return maxY;
|
||||
}
|
||||
|
||||
|
||||
public static SkillTree loadSkillTree(ConfigurationSection config) {
|
||||
String string = config.getString("type");
|
||||
|
||||
Validate.isTrue(string.equals("automatic") || string.equals("linked") || string.equals("custom"), "You must precise the type of the skill tree in the yml!" +
|
||||
"\nAllowed values: 'automatic','linked','custom'");
|
||||
SkillTree skillTree = null;
|
||||
if (string.equals("automatic")) {
|
||||
skillTree = new AutomaticSkillTree(config);
|
||||
skillTree.postLoad();
|
||||
}
|
||||
if (string.equals("linked")) {
|
||||
skillTree = new LinkedSkillTree(config);
|
||||
skillTree.postLoad();
|
||||
}
|
||||
if (string.equals("custom")) {
|
||||
skillTree = new CustomSkillTree(config);
|
||||
skillTree.postLoad();
|
||||
}
|
||||
|
||||
return skillTree;
|
||||
}
|
||||
|
||||
@Nullable
|
||||
/**
|
||||
* Returns null if it is not a node and returns the node type if it a node
|
||||
*/
|
||||
public boolean isNode(IntegerCoordinates coordinates) {
|
||||
for (SkillTreeNode node : nodes.values()) {
|
||||
if (node.getCoordinates().equals(coordinates))
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
public abstract boolean isPath(IntegerCoordinates coordinates);
|
||||
|
||||
public Material getGuiMaterial() {
|
||||
return guiMaterial;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getId() {
|
||||
return id;
|
||||
}
|
||||
|
||||
public String getName() {
|
||||
return name;
|
||||
}
|
||||
|
||||
public Collection<SkillTreeNode> getNodes() {
|
||||
return nodes.values();
|
||||
}
|
||||
|
||||
@NotNull
|
||||
public SkillTreeNode getNode(IntegerCoordinates coords) {
|
||||
return Objects.requireNonNull(nodes.get(coords), "Could not find node in tree '" + id + "' with coordinates '" + coords.toString() + "'");
|
||||
}
|
||||
|
||||
@NotNull
|
||||
public SkillTreeNode getNode(String name) {
|
||||
return Objects.requireNonNull(nodes.get(name), "Could not find node in tree '" + id + "' with name '" + name + "'");
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean equals(Object o) {
|
||||
if (this == o) return true;
|
||||
if (o == null || getClass() != o.getClass()) return false;
|
||||
SkillTree skillTree = (SkillTree) o;
|
||||
return id.equals(skillTree.id);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int hashCode() {
|
||||
return Objects.hash(id);
|
||||
}
|
||||
}
|
@ -0,0 +1,7 @@
|
||||
package net.Indyuce.mmocore.tree.skilltree;
|
||||
|
||||
public enum SkillTreeType {
|
||||
AUTOMATIC_SKILL_TREE(),
|
||||
LINKED_SKILL_TREE,
|
||||
CUSTOM_SKILL_TREE;
|
||||
}
|
@ -28,6 +28,9 @@ guild:
|
||||
withdraw:
|
||||
main: "withdraw"
|
||||
aliases: ["w"]
|
||||
skill-tree:
|
||||
main: "skilltree"
|
||||
aliase: ["st"]
|
||||
deposit:
|
||||
main: "deposit"
|
||||
aliases: ["d"]
|
25
src/main/resources/default/gui/skill-tree.yml
Normal file
25
src/main/resources/default/gui/skill-tree.yml
Normal file
@ -0,0 +1,25 @@
|
||||
# GUI display name
|
||||
name: 'Current Skill Tree: &6{skill-tree-name}'
|
||||
|
||||
# Number of slots in your inventory. Must be
|
||||
# between 9 and 54 and must be a multiple of 9.
|
||||
slots: 54
|
||||
|
||||
items:
|
||||
skill-tree:
|
||||
name: '{skill-tree-node}'
|
||||
function: 'skill-tree'
|
||||
slots: [9,18,27,36]
|
||||
|
||||
next-tree-list-page:
|
||||
function: 'next-tree-list-page'
|
||||
material: 'ARROW'
|
||||
slots: [ 45 ]
|
||||
|
||||
previous-tree-list-page:
|
||||
function: 'previous-tree-list-page'
|
||||
material: "ARROW"
|
||||
slots: [ 0 ]
|
||||
|
||||
skill-tree-node:
|
||||
function: 'skill-tree-node'
|
29
src/main/resources/default/skilltree/combat.yml
Normal file
29
src/main/resources/default/skilltree/combat.yml
Normal file
@ -0,0 +1,29 @@
|
||||
id: 'combat'
|
||||
name: 'Combat Skill Tree'
|
||||
|
||||
|
||||
#The type of the Skill tree, can be :
|
||||
#'linked'->You must only precise nodes coordinates. 2 adjacent nodes will be affiliated. The root is at coordinates 0,0.
|
||||
#'automatic'-> You must only precise the children(there can more than 1) of each node. Each node can only have one parent and the root has none.
|
||||
# The display coordinates will be automatically calculated to have a good render.
|
||||
#'custom'-> You must precise coordinates and children for each node, each node can have multiple parents and children.
|
||||
# The coordinates are only used for display on the GUI but will not have any impact on the affiliation between nodes.
|
||||
|
||||
type: 'linked'
|
||||
|
||||
#The material that will represent the skill tree in the GUI
|
||||
material: 'DIAMOND_SWORD'
|
||||
|
||||
nodes:
|
||||
strength:
|
||||
name: 'Combat strength'
|
||||
#Coordinates of the node
|
||||
coordinates:
|
||||
x: 0
|
||||
y: 0
|
||||
strength2:
|
||||
name: 'Combat strength 2'
|
||||
coordinates:
|
||||
x: 1
|
||||
y: 0
|
||||
|
Loading…
Reference in New Issue
Block a user