mirror of
https://gitlab.com/phoenix-dvpmt/mmocore.git
synced 2024-11-23 00:05:52 +01:00
Merge remote-tracking branch 'origin/master'
# Conflicts: # MMOCore-API/src/main/java/net/Indyuce/mmocore/api/player/PlayerData.java # MMOCore-API/src/main/java/net/Indyuce/mmocore/skill/binding/BoundSkillInfo.java # MMOCore-API/src/main/java/net/Indyuce/mmocore/skill/cast/listener/SkillBar.java # MMOCore-Dist/src/main/resources/config.yml
This commit is contained in:
commit
ac36e3eaa1
@ -27,6 +27,7 @@ import net.Indyuce.mmocore.api.player.social.FriendRequest;
|
||||
import net.Indyuce.mmocore.api.player.stats.PlayerStats;
|
||||
import net.Indyuce.mmocore.api.quest.PlayerQuests;
|
||||
import net.Indyuce.mmocore.api.quest.trigger.StatTrigger;
|
||||
import net.Indyuce.mmocore.api.quest.trigger.Trigger;
|
||||
import net.Indyuce.mmocore.api.util.Closable;
|
||||
import net.Indyuce.mmocore.api.util.MMOCoreUtils;
|
||||
import net.Indyuce.mmocore.experience.EXPSource;
|
||||
@ -45,15 +46,15 @@ import net.Indyuce.mmocore.player.CombatHandler;
|
||||
import net.Indyuce.mmocore.player.Unlockable;
|
||||
import net.Indyuce.mmocore.skill.ClassSkill;
|
||||
import net.Indyuce.mmocore.skill.RegisteredSkill;
|
||||
import net.Indyuce.mmocore.skill.binding.BoundSkillInfo;
|
||||
import net.Indyuce.mmocore.skill.binding.SkillSlot;
|
||||
import net.Indyuce.mmocore.skill.cast.SkillCastingInstance;
|
||||
import net.Indyuce.mmocore.skill.cast.SkillCastingMode;
|
||||
import net.Indyuce.mmocore.skilltree.IntegerCoordinates;
|
||||
import net.Indyuce.mmocore.skill.cast.SkillCastingHandler;
|
||||
import net.Indyuce.mmocore.skilltree.NodeStatus;
|
||||
import net.Indyuce.mmocore.skilltree.SkillTreeNode;
|
||||
import net.Indyuce.mmocore.skilltree.tree.SkillTree;
|
||||
import net.Indyuce.mmocore.skilltree.tree.display.DisplayInfo;
|
||||
import net.Indyuce.mmocore.skilltree.tree.display.Icon;
|
||||
import net.Indyuce.mmocore.waypoint.Waypoint;
|
||||
import net.Indyuce.mmocore.waypoint.WaypointOption;
|
||||
import net.md_5.bungee.api.ChatMessageType;
|
||||
@ -269,7 +270,7 @@ public class PlayerData extends SynchronizedDataHolder implements OfflinePlayerD
|
||||
NodeStatus nodeStatus = nodeStates.get(node);
|
||||
//Check the State of the node
|
||||
if (nodeStatus != NodeStatus.UNLOCKED && nodeStatus != NodeStatus.UNLOCKABLE) return false;
|
||||
return getNodeLevel(node) < node.getMaxLevel() && (skillTreePoints.getOrDefault(node.getTree().getId(), 0) + skillTreePoints.getOrDefault("global", 0) >= node.getSkillTreePointsConsumed());
|
||||
return node.hasPermissionRequirement(this)&&getNodeLevel(node) < node.getMaxLevel() && (skillTreePoints.getOrDefault(node.getTree().getId(), 0) + skillTreePoints.getOrDefault("global", 0) >= node.getSkillTreePointsConsumed());
|
||||
}
|
||||
|
||||
/**
|
||||
@ -296,27 +297,6 @@ public class PlayerData extends SynchronizedDataHolder implements OfflinePlayerD
|
||||
node.getTree().setupNodeStates(this);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the icon the node should have.
|
||||
*/
|
||||
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 getSkillTreePoint(String treeId) {
|
||||
return skillTreePoints.getOrDefault(treeId, 0);
|
||||
@ -330,7 +310,7 @@ public class PlayerData extends SynchronizedDataHolder implements OfflinePlayerD
|
||||
nodeStates.put(node, nodeStatus);
|
||||
}
|
||||
|
||||
public NodeStatus getNodeState(SkillTreeNode node) {
|
||||
public NodeStatus getNodeStatus(SkillTreeNode node) {
|
||||
return nodeStates.get(node);
|
||||
}
|
||||
|
||||
@ -389,6 +369,7 @@ public class PlayerData extends SynchronizedDataHolder implements OfflinePlayerD
|
||||
*/
|
||||
public boolean unlock(Unlockable unlockable) {
|
||||
Validate.isTrue(!unlockable.isUnlockedByDefault(), "Cannot unlock an item unlocked by default");
|
||||
unlockable.whenUnlocked(this);
|
||||
final boolean wasLocked = unlockedItems.add(unlockable.getUnlockNamespacedKey());
|
||||
// Call the event synchronously
|
||||
if (wasLocked)
|
||||
@ -404,6 +385,7 @@ public class PlayerData extends SynchronizedDataHolder implements OfflinePlayerD
|
||||
*/
|
||||
public boolean lock(Unlockable unlockable) {
|
||||
Validate.isTrue(!unlockable.isUnlockedByDefault(), "Cannot lock an item unlocked by default");
|
||||
unlockable.whenLocked(this);
|
||||
boolean wasUnlocked = unlockedItems.remove(unlockable.getUnlockNamespacedKey());
|
||||
if (wasUnlocked)
|
||||
//Calls the event synchronously
|
||||
@ -819,7 +801,8 @@ public class PlayerData extends SynchronizedDataHolder implements OfflinePlayerD
|
||||
* If it's null, no hologram will be displayed
|
||||
* @param splitExp Should the exp be split among party members
|
||||
*/
|
||||
public void giveExperience(double value, @NotNull EXPSource source, @Nullable Location hologramLocation, boolean splitExp) {
|
||||
public void giveExperience(double value, @NotNull EXPSource source, @Nullable Location hologramLocation,
|
||||
boolean splitExp) {
|
||||
if (value <= 0) {
|
||||
experience = Math.max(0, experience + value);
|
||||
return;
|
||||
@ -872,7 +855,8 @@ public class PlayerData extends SynchronizedDataHolder implements OfflinePlayerD
|
||||
level = getLevel() + 1;
|
||||
|
||||
// Apply class experience table
|
||||
if (getProfess().hasExperienceTable()) getProfess().getExperienceTable().claim(this, level, getProfess());
|
||||
if (getProfess().hasExperienceTable())
|
||||
getProfess().getExperienceTable().claim(this, level, getProfess());
|
||||
}
|
||||
|
||||
if (level > oldLevel) {
|
||||
@ -898,10 +882,6 @@ public class PlayerData extends SynchronizedDataHolder implements OfflinePlayerD
|
||||
return profess == null ? MMOCore.plugin.classManager.getDefaultClass() : profess;
|
||||
}
|
||||
|
||||
public boolean isProfessNull() {
|
||||
return profess == null;
|
||||
}
|
||||
|
||||
/**
|
||||
* @deprecated Provide reason with {@link #giveMana(double, PlayerResourceUpdateEvent.UpdateReason)}
|
||||
*/
|
||||
@ -1094,6 +1074,11 @@ public class PlayerData extends SynchronizedDataHolder implements OfflinePlayerD
|
||||
return getProfess().hasSkill(skill.getHandler().getId()) && hasSkillUnlocked(getProfess().getSkill(skill.getHandler().getId()));
|
||||
}
|
||||
|
||||
@Deprecated
|
||||
public boolean hasSkillUnlocked(ClassSkill skill) {
|
||||
return hasUnlockedLevel(skill);
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks for the player's level and compares it to the skill unlock level.
|
||||
* <p>
|
||||
@ -1104,7 +1089,7 @@ public class PlayerData extends SynchronizedDataHolder implements OfflinePlayerD
|
||||
*
|
||||
* @return If the player unlocked the skill
|
||||
*/
|
||||
public boolean hasSkillUnlocked(ClassSkill skill) {
|
||||
public boolean hasUnlockedLevel(ClassSkill skill){
|
||||
return getLevel() >= skill.getUnlockLevel();
|
||||
}
|
||||
|
||||
|
@ -293,6 +293,13 @@ public class SavedClassInformation implements ClassDataContainer {
|
||||
if (!player.getProfess().hasOption(ClassOption.DEFAULT) || MMOCore.plugin.configManager.saveDefaultClassInfo)
|
||||
player.applyClassInfo(player.getProfess(), new SavedClassInformation(player));
|
||||
|
||||
// Remove perm stats for nodes and class
|
||||
for (SkillTree skillTree : player.getProfess().getSkillTrees())
|
||||
for (SkillTreeNode node : skillTree.getNodes())
|
||||
node.getExperienceTable().removePermStats(player, node);
|
||||
if (player.getProfess().hasExperienceTable())
|
||||
player.getProfess().getExperienceTable().removePermStats(player, player.getProfess());
|
||||
|
||||
/*
|
||||
* Resets information which much be reset after everything is saved.
|
||||
*/
|
||||
@ -303,13 +310,6 @@ public class SavedClassInformation implements ClassDataContainer {
|
||||
player.clearNodeStates();
|
||||
player.clearNodeTimesClaimed();
|
||||
|
||||
// Remove perm stats for nodes and class
|
||||
for (SkillTree skillTree : player.getProfess().getSkillTrees())
|
||||
for (SkillTreeNode node : skillTree.getNodes())
|
||||
node.getExperienceTable().removePermStats(player, node);
|
||||
if (player.getProfess().hasExperienceTable())
|
||||
player.getProfess().getExperienceTable().removePermStats(player, player.getProfess());
|
||||
|
||||
/*
|
||||
* Reads this class info, applies it to the player. set class after
|
||||
* changing level so the player stats can be calculated based on new level
|
||||
|
@ -14,6 +14,10 @@ import net.Indyuce.mmocore.experience.Profession;
|
||||
import net.Indyuce.mmocore.player.stats.StatInfo;
|
||||
import net.Indyuce.mmocore.skill.ClassSkill;
|
||||
|
||||
import java.util.Comparator;
|
||||
import java.util.List;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
public class PlayerStats {
|
||||
private final PlayerData data;
|
||||
|
||||
@ -99,12 +103,14 @@ public class PlayerStats {
|
||||
*/
|
||||
final PassiveSkillMap skillMap = data.getMMOPlayerData().getPassiveSkillMap();
|
||||
|
||||
//if (!MMOCore.plugin.configManager.passiveSkillNeedBound) {
|
||||
// skillMap.removeModifiers("MMOCorePassiveSkill");
|
||||
// for (ClassSkill skill : data.getProfess().getSkills())
|
||||
// if (skill.getSkill().getTrigger().isPassive())
|
||||
// skillMap.addModifier(skill.toPassive(data));
|
||||
//}
|
||||
if (!MMOCore.plugin.configManager.passiveSkillNeedBound) {
|
||||
skillMap.removeModifiers("MMOCorePassiveSkill");
|
||||
data.getProfess().getSkills()
|
||||
.stream()
|
||||
.filter((classSkill) -> classSkill.getSkill().getTrigger().isPassive() && data.hasUnlocked(classSkill) && data.hasUnlockedLevel(classSkill))
|
||||
.forEach(classSkill -> skillMap.addModifier(classSkill.toPassive(data)));
|
||||
|
||||
}
|
||||
|
||||
// This updates the player's class SCRIPTS
|
||||
skillMap.removeModifiers("MMOCoreClassScript");
|
||||
|
@ -14,7 +14,11 @@ public class UnlockSlotTrigger extends Trigger implements Removable {
|
||||
public UnlockSlotTrigger(MMOLineConfig config) {
|
||||
super(config);
|
||||
config.validateKeys("slot");
|
||||
slot = Integer.parseInt("slot");
|
||||
try {
|
||||
slot = Integer.parseInt(config.getString("slot"));
|
||||
}catch(NumberFormatException e){
|
||||
throw new IllegalArgumentException("The slot should be a number");
|
||||
}
|
||||
Validate.isTrue(slot > 0 && slot <= MMOCore.plugin.configManager.maxSkillSlots, "The slot should be between 1 and " + MMOCore.plugin.configManager.maxSkillSlots);
|
||||
}
|
||||
|
||||
|
@ -53,7 +53,15 @@ public class SlotCommandTreeNode extends CommandTreeNode {
|
||||
sender.sendMessage(ChatColor.RED + "The slot can't be negative.");
|
||||
return CommandResult.FAILURE;
|
||||
}
|
||||
if (slot > MMOCore.plugin.configManager.maxSkillSlots) {
|
||||
sender.sendMessage(ChatColor.RED + "The slot can't be higher than " + MMOCore.plugin.configManager.maxSkillSlots + ".");
|
||||
return CommandResult.FAILURE;
|
||||
}
|
||||
SkillSlot skillSlot = playerData.getProfess().getSkillSlot(slot);
|
||||
if(skillSlot.isUnlockedByDefault()){
|
||||
sender.sendMessage(ChatColor.RED + "You can't lock a skill that is unlocked by default.");
|
||||
return CommandResult.FAILURE;
|
||||
}
|
||||
if (lock) {
|
||||
if (!playerData.hasUnlocked(skillSlot)) {
|
||||
CommandVerbose.verbose(sender, CommandVerbose.CommandType.SKILL, ChatColor.RED + "The skill slot " + skillSlot.getName() + " is already locked" + " for " + player.getName());
|
||||
|
@ -123,7 +123,7 @@ public class RPGPlaceholders extends PlaceholderExpansion {
|
||||
return playerData.isInCombat() ? MythicLib.plugin.getMMOConfig().decimal.format((System.currentTimeMillis() - playerData.getCombat().getLastHit()) / 1000.) : "-1";
|
||||
|
||||
else if (identifier.startsWith("bound_")) {
|
||||
int slot = Math.max(0, Integer.parseInt(identifier.substring(6)) - 1);
|
||||
int slot = Math.max(0, Integer.parseInt(identifier.substring(6)));
|
||||
return playerData.hasSkillBound(slot) ? playerData.getBoundSkill(slot).getSkill().getName()
|
||||
: MMOCore.plugin.configManager.noSkillBoundPlaceholder;
|
||||
|
||||
|
@ -21,6 +21,7 @@ import net.Indyuce.mmocore.player.stats.StatInfo;
|
||||
import net.Indyuce.mmocore.api.player.attribute.PlayerAttribute;
|
||||
import org.apache.commons.lang.Validate;
|
||||
import org.bukkit.ChatColor;
|
||||
import org.bukkit.OfflinePlayer;
|
||||
import org.bukkit.configuration.ConfigurationSection;
|
||||
import org.bukkit.entity.Player;
|
||||
import org.bukkit.inventory.ItemStack;
|
||||
@ -122,9 +123,13 @@ public class PlayerStats extends EditableInventory {
|
||||
return new Placeholders() {
|
||||
final net.Indyuce.mmocore.api.player.stats.PlayerStats stats = inv.target.getStats();
|
||||
|
||||
public String apply(Player player, String str) {
|
||||
while (str.contains("{") && str.substring(str.indexOf("{")).contains("}")) {
|
||||
final String holder = str.substring(str.indexOf("{") + 1, str.indexOf("}"));
|
||||
@Override
|
||||
public String apply(OfflinePlayer player, String str) {
|
||||
String explored = str;
|
||||
// Internal placeholders
|
||||
while (explored.contains("{") && explored.substring(explored.indexOf("{")).contains("}")) {
|
||||
final int begin = explored.indexOf("{"), end = explored.indexOf("}");
|
||||
final String holder = explored.substring(begin + 1, end);
|
||||
String replaced;
|
||||
if (holder.endsWith("_base")) {
|
||||
final String stat = UtilityMethods.enumName(holder.substring(0, holder.length() - 5));
|
||||
@ -141,6 +146,9 @@ public class PlayerStats extends EditableInventory {
|
||||
}
|
||||
|
||||
str = str.replace("{" + holder + "}", replaced);
|
||||
|
||||
// Increase counter
|
||||
explored = explored.substring(end + 1);
|
||||
}
|
||||
|
||||
// External placeholders
|
||||
|
@ -3,7 +3,6 @@ package net.Indyuce.mmocore.gui;
|
||||
import io.lumine.mythic.lib.MythicLib;
|
||||
import io.lumine.mythic.lib.api.item.ItemTag;
|
||||
import io.lumine.mythic.lib.api.item.NBTItem;
|
||||
import me.ulrich.clans.manager.I;
|
||||
import net.Indyuce.mmocore.MMOCore;
|
||||
import net.Indyuce.mmocore.api.SoundEvent;
|
||||
import net.Indyuce.mmocore.api.player.PlayerData;
|
||||
@ -193,6 +192,7 @@ public class SkillList extends EditableInventory {
|
||||
|
||||
final @Nullable ClassSkill boundSkill = inv.getPlayerData().getBoundSkill(n + 1);
|
||||
final ItemStack item = super.display(inv, n);
|
||||
Placeholders holders = getPlaceholders(inv, n);
|
||||
|
||||
// Same material as skill
|
||||
if (boundSkill != null)
|
||||
@ -201,6 +201,17 @@ public class SkillList extends EditableInventory {
|
||||
final ItemMeta meta = item.getItemMeta();
|
||||
meta.setDisplayName(MMOCore.plugin.placeholderParser.parse(inv.getPlayerData().getPlayer(), skillSlot.getName()));
|
||||
|
||||
List<String> lore = new ArrayList<>(getLore());
|
||||
|
||||
int index = lore.indexOf("{slot-lore}");
|
||||
lore.remove(index);
|
||||
List<String> slotLore = skillSlot.getLore();
|
||||
for (int j = 0; j < slotLore.size(); j++)
|
||||
lore.add(index + j, slotLore.get(j));
|
||||
|
||||
for (int j = 0; j < lore.size(); j++)
|
||||
lore.set(j, ChatColor.GRAY + holders.apply(inv.getPlayer(), lore.get(j)));
|
||||
meta.setLore(lore);
|
||||
// Same CMD as skill icon
|
||||
if (boundSkill != null && boundSkill.getSkill().getIcon().hasItemMeta() && boundSkill.getSkill().getIcon().getItemMeta().hasCustomModelData())
|
||||
meta.setCustomModelData(boundSkill.getSkill().getIcon().getItemMeta().getCustomModelData());
|
||||
@ -209,17 +220,12 @@ public class SkillList extends EditableInventory {
|
||||
return item;
|
||||
}
|
||||
|
||||
/**
|
||||
* This should only be called when there is a skill bound.
|
||||
*/
|
||||
@Override
|
||||
public Placeholders getPlaceholders(SkillViewerInventory inv, int n) {
|
||||
RegisteredSkill selected = inv.selected.getSkill();
|
||||
|
||||
final @NotNull SkillSlot skillSlot = inv.getPlayerData().getProfess().getSkillSlot(n + 1);
|
||||
Placeholders holders = new Placeholders();
|
||||
|
||||
holders.register("index", "" + (n + 1));
|
||||
holders.register("slot", MMOCoreUtils.intToRoman(n + 1));
|
||||
holders.register("slot", skillSlot.getName());
|
||||
holders.register("selected", selected == null ? none : selected.getName());
|
||||
RegisteredSkill skill = inv.getPlayerData().hasSkillBound(n + 1) ? inv.getPlayerData().getBoundSkill(n + 1).getSkill() : null;
|
||||
holders.register("skill", skill == null ? none : skill.getName());
|
||||
@ -434,7 +440,7 @@ public class SkillList extends EditableInventory {
|
||||
return;
|
||||
}
|
||||
|
||||
if (!playerData.hasSkillUnlocked(selected)) {
|
||||
if (!playerData.hasUnlockedLevel(selected)) {
|
||||
MMOCore.plugin.configManager.getSimpleMessage("skill-level-not-met").send(player);
|
||||
player.playSound(player.getLocation(), Sound.ENTITY_VILLAGER_NO, 1, 2);
|
||||
return;
|
||||
@ -462,8 +468,8 @@ public class SkillList extends EditableInventory {
|
||||
if (item.getFunction().equals("upgrade")) {
|
||||
int shiftCost = ((UpgradeItem) item).shiftCost;
|
||||
|
||||
if (!playerData.hasSkillUnlocked(selected)) {
|
||||
MMOCore.plugin.configManager.getSimpleMessage("not-unlocked-skill").send(player);
|
||||
if (!playerData.hasUnlockedLevel(selected)) {
|
||||
MMOCore.plugin.configManager.getSimpleMessage("skill-level-not-met").send(player);
|
||||
player.playSound(player.getLocation(), Sound.ENTITY_VILLAGER_NO, 1, 2);
|
||||
return;
|
||||
}
|
||||
|
@ -16,6 +16,7 @@ import net.Indyuce.mmocore.api.ConfigMessage;
|
||||
import net.Indyuce.mmocore.api.SoundEvent;
|
||||
import net.Indyuce.mmocore.api.player.profess.Subclass;
|
||||
import org.apache.commons.lang.Validate;
|
||||
import org.bukkit.Bukkit;
|
||||
import org.bukkit.Material;
|
||||
import org.bukkit.NamespacedKey;
|
||||
import org.bukkit.configuration.ConfigurationSection;
|
||||
@ -112,8 +113,8 @@ public class SubclassSelect extends EditableInventory {
|
||||
InventoryManager.CLASS_SELECT.newInventory(playerData).open();
|
||||
|
||||
if (item.getFunction().startsWith("sub-class")) {
|
||||
String classId = item.getFunction().substring(10);
|
||||
PlayerClass profess = MMOCore.plugin.classManager.get(classId);
|
||||
String classId = UtilityMethods.ymlName(item.getFunction().substring(10));
|
||||
PlayerClass profess = MMOCore.plugin.classManager.get(UtilityMethods.enumName(classId));
|
||||
if (playerData.getClassPoints() < 1) {
|
||||
player.closeInventory();
|
||||
MMOCore.plugin.soundManager.getSound(SoundEvent.CANT_SELECT_CLASS).playTo(getPlayer());
|
||||
|
@ -1,24 +1,26 @@
|
||||
package net.Indyuce.mmocore.gui;
|
||||
package net.Indyuce.mmocore.gui.skilltree;
|
||||
|
||||
import io.lumine.mythic.lib.MythicLib;
|
||||
import net.Indyuce.mmocore.MMOCore;
|
||||
import net.Indyuce.mmocore.api.player.PlayerData;
|
||||
import net.Indyuce.mmocore.skilltree.NodeStatus;
|
||||
import net.Indyuce.mmocore.skilltree.tree.SkillTree;
|
||||
import net.Indyuce.mmocore.skilltree.tree.display.Icon;
|
||||
import net.Indyuce.mmocore.api.SoundEvent;
|
||||
import net.Indyuce.mmocore.api.player.PlayerData;
|
||||
import net.Indyuce.mmocore.api.util.MMOCoreUtils;
|
||||
import net.Indyuce.mmocore.gui.api.EditableInventory;
|
||||
import net.Indyuce.mmocore.gui.api.GeneratedInventory;
|
||||
import net.Indyuce.mmocore.gui.api.InventoryClickContext;
|
||||
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.skilltree.IntegerCoordinates;
|
||||
import net.Indyuce.mmocore.skilltree.SkillTreeNode;
|
||||
import net.Indyuce.mmocore.gui.skilltree.display.*;
|
||||
import net.Indyuce.mmocore.skilltree.*;
|
||||
import net.Indyuce.mmocore.skilltree.tree.SkillTree;
|
||||
import org.apache.commons.lang.Validate;
|
||||
import org.bukkit.Bukkit;
|
||||
import org.bukkit.ChatColor;
|
||||
import org.bukkit.Material;
|
||||
import org.bukkit.NamespacedKey;
|
||||
import org.bukkit.configuration.ConfigurationSection;
|
||||
import org.bukkit.configuration.file.FileConfiguration;
|
||||
import org.bukkit.event.inventory.ClickType;
|
||||
import org.bukkit.inventory.ItemFlag;
|
||||
import org.bukkit.inventory.ItemStack;
|
||||
@ -26,17 +28,38 @@ import org.bukkit.inventory.meta.ItemMeta;
|
||||
import org.bukkit.persistence.PersistentDataContainer;
|
||||
import org.bukkit.persistence.PersistentDataType;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collection;
|
||||
import java.util.List;
|
||||
import java.util.*;
|
||||
|
||||
public class SkillTreeViewer extends EditableInventory {
|
||||
|
||||
protected final Map<DisplayInfo, Icon> icons = new HashMap<>();
|
||||
|
||||
public SkillTreeViewer() {
|
||||
super("skill-tree");
|
||||
}
|
||||
|
||||
@Override
|
||||
public void reload(FileConfiguration config) {
|
||||
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()))) {
|
||||
MMOCore.log("Missing path type: " + MMOCoreUtils.ymlName(pathType.name()) + " for status: " + MMOCoreUtils.ymlName(status.name()));
|
||||
continue;
|
||||
}
|
||||
icons.put(new PathDisplayInfo(pathType, status), new Icon(config.getConfigurationSection("display.paths." + MMOCoreUtils.ymlName(status.name()) + "." + MMOCoreUtils.ymlName(pathType.name()))));
|
||||
}
|
||||
//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()))) {
|
||||
MMOCore.log("Missing node type: " + MMOCoreUtils.ymlName(nodeType.name()) + " for status: " + MMOCoreUtils.ymlName(status.name()));
|
||||
continue;
|
||||
}
|
||||
icons.put(new NodeDisplayInfo(nodeType, status), new Icon(config.getConfigurationSection("display.nodes." + MMOCoreUtils.ymlName(status.name()) + "." + MMOCoreUtils.ymlName(nodeType.name()))));
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public InventoryItem load(String function, ConfigurationSection config) {
|
||||
if (function.equals("skill-tree")) {
|
||||
@ -190,7 +213,7 @@ public class SkillTreeViewer extends EditableInventory {
|
||||
public ItemStack display(SkillTreeInventory inv, int n) {
|
||||
IntegerCoordinates coordinates = inv.getCoordinates(n);
|
||||
if (inv.getSkillTree().isNode(coordinates) || inv.getSkillTree().isPath(coordinates)) {
|
||||
Icon icon = inv.getPlayerData().getIcon(inv.getSkillTree(), coordinates);
|
||||
Icon icon = inv.getIcon(coordinates);
|
||||
ItemStack item = super.display(inv, n, icon.getMaterial(), icon.getCustomModelData());
|
||||
ItemMeta meta = item.getItemMeta();
|
||||
if (inv.getSkillTree().isNode(coordinates)) {
|
||||
@ -201,9 +224,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));
|
||||
});
|
||||
@ -247,7 +272,7 @@ public class SkillTreeViewer extends EditableInventory {
|
||||
if (inv.getSkillTree().isNode(inv.getCoordinates(n))) {
|
||||
SkillTreeNode node = inv.getNode(n);
|
||||
holders.register("current-level", inv.getPlayerData().getNodeLevel(node));
|
||||
holders.register("current-state", inv.getPlayerData().getNodeState(node));
|
||||
holders.register("current-state", inv.getPlayerData().getNodeStatus(node));
|
||||
holders.register("max-level", node.getMaxLevel());
|
||||
holders.register("max-children", node.getMaxChildren());
|
||||
holders.register("size", node.getSize());
|
||||
@ -313,6 +338,33 @@ public class SkillTreeViewer extends EditableInventory {
|
||||
return maxTreeListPage;
|
||||
}
|
||||
|
||||
|
||||
public Icon getIcon(IntegerCoordinates coordinates) {
|
||||
boolean hasUpPath = skillTree.isPath(new IntegerCoordinates(coordinates.getX(), coordinates.getY() - 1));
|
||||
boolean hasDownPath = skillTree.isPath(new IntegerCoordinates(coordinates.getX(), coordinates.getY() + 1));
|
||||
boolean hasRightPath = skillTree.isPath(new IntegerCoordinates(coordinates.getX() + 1, coordinates.getY()));
|
||||
boolean hasLeftPath = skillTree.isPath(new IntegerCoordinates(coordinates.getX() - 1, coordinates.getY()));
|
||||
|
||||
if (skillTree.isNode(coordinates)) {
|
||||
SkillTreeNode node = skillTree.getNode(coordinates);
|
||||
NodeStatus nodeStatus = playerData.getNodeStatus(node);
|
||||
//If the node has its own display, it will be shown.
|
||||
if (node.hasIcon(nodeStatus))
|
||||
return node.getIcon(nodeStatus);
|
||||
|
||||
NodeType nodeType = NodeType.getNodeType(hasUpPath, hasRightPath, hasDownPath, hasLeftPath);
|
||||
Icon icon = icons.get(new NodeDisplayInfo(nodeType, nodeStatus));
|
||||
Validate.notNull(icon, "The node " + node.getFullId() + " has no icon for the type " + nodeType + " and the status " + nodeStatus);
|
||||
return icon;
|
||||
} else {
|
||||
PathType pathType = PathType.getPathType(hasUpPath, hasRightPath, hasDownPath, hasLeftPath);
|
||||
SkillTreePath path = skillTree.getPath(coordinates);
|
||||
Icon icon = icons.get(new PathDisplayInfo(pathType, path.getStatus(playerData)));
|
||||
Validate.notNull(icon, "There is no icon for the path type " + pathType + " and the status " + path.getStatus(playerData));
|
||||
return icon;
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public String calculateName() {
|
||||
return getEditable().getName().replace("{skill-tree-name}", skillTree.getName()).replace("{skill-tree-id}", skillTree.getId());
|
||||
@ -375,14 +427,12 @@ public class SkillTreeViewer extends EditableInventory {
|
||||
if (spent < 1) {
|
||||
MMOCore.plugin.configManager.getSimpleMessage("no-skill-tree-points-spent").send(player);
|
||||
MMOCore.plugin.soundManager.getSound(SoundEvent.NOT_ENOUGH_POINTS).playTo(getPlayer());
|
||||
event.setCancelled(true);
|
||||
return;
|
||||
}
|
||||
|
||||
if (getPlayerData().getSkillTreeReallocationPoints() <= 0) {
|
||||
MMOCore.plugin.configManager.getSimpleMessage("not-skill-tree-reallocation-point").send(player);
|
||||
MMOCore.plugin.soundManager.getSound(SoundEvent.NOT_ENOUGH_POINTS).playTo(getPlayer());
|
||||
event.setCancelled(true);
|
||||
return;
|
||||
} else {
|
||||
int reallocated = playerData.getPointSpent(skillTree);
|
||||
@ -393,7 +443,6 @@ public class SkillTreeViewer extends EditableInventory {
|
||||
skillTree.setupNodeStates(playerData);
|
||||
MMOCore.plugin.configManager.getSimpleMessage("reallocated-points", "points", "" + playerData.getSkillTreePoint(skillTree.getId()), "skill-tree", skillTree.getName()).send(player);
|
||||
MMOCore.plugin.soundManager.getSound(SoundEvent.RESET_SKILL_TREE).playTo(player);
|
||||
event.setCancelled(true);
|
||||
open();
|
||||
return;
|
||||
|
||||
@ -406,7 +455,6 @@ public class SkillTreeViewer extends EditableInventory {
|
||||
MMOCore.plugin.soundManager.getSound(SoundEvent.CHANGE_SKILL_TREE).playTo(player);
|
||||
skillTree = MMOCore.plugin.skillTreeManager.get(id);
|
||||
open();
|
||||
event.setCancelled(true);
|
||||
return;
|
||||
}
|
||||
|
||||
@ -416,16 +464,13 @@ public class SkillTreeViewer extends EditableInventory {
|
||||
int x = container.get(new NamespacedKey(MMOCore.plugin, "coordinates.x"), PersistentDataType.INTEGER);
|
||||
int y = container.get(new NamespacedKey(MMOCore.plugin, "coordinates.y"), PersistentDataType.INTEGER);
|
||||
if (!skillTree.isNode(new IntegerCoordinates(x, y))) {
|
||||
event.setCancelled(true);
|
||||
return;
|
||||
}
|
||||
SkillTreeNode node = skillTree.getNode(new IntegerCoordinates(x, y));
|
||||
if (playerData.getPointSpent(skillTree) >= skillTree.getMaxPointSpent()) {
|
||||
MMOCore.plugin.configManager.getSimpleMessage("max-points-reached").send(player);
|
||||
MMOCore.plugin.soundManager.getSound(SoundEvent.NOT_ENOUGH_POINTS).playTo(getPlayer());
|
||||
event.setCancelled(true);
|
||||
return;
|
||||
|
||||
}
|
||||
|
||||
if (playerData.canIncrementNodeLevel(node)) {
|
||||
@ -433,27 +478,22 @@ public class SkillTreeViewer extends EditableInventory {
|
||||
MMOCore.plugin.configManager.getSimpleMessage("upgrade-skill-node", "skill-node", node.getName(), "level", "" + playerData.getNodeLevel(node)).send(player);
|
||||
MMOCore.plugin.soundManager.getSound(SoundEvent.LEVEL_UP).playTo(getPlayer());
|
||||
open();
|
||||
event.setCancelled(true);
|
||||
return;
|
||||
} else if (playerData.getNodeState(node) == NodeStatus.LOCKED || playerData.getNodeState(node) == NodeStatus.FULLY_LOCKED) {
|
||||
} else if (playerData.getNodeStatus(node) == NodeStatus.LOCKED || playerData.getNodeStatus(node) == NodeStatus.FULLY_LOCKED) {
|
||||
MMOCore.plugin.configManager.getSimpleMessage("locked-node").send(player);
|
||||
MMOCore.plugin.soundManager.getSound(SoundEvent.NOT_ENOUGH_POINTS).playTo(getPlayer());
|
||||
event.setCancelled(true);
|
||||
return;
|
||||
|
||||
} 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());
|
||||
event.setCancelled(true);
|
||||
return;
|
||||
} 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());
|
||||
}
|
||||
|
||||
//Else the player doesn't doesn't have the skill tree points
|
||||
else {
|
||||
MMOCore.plugin.configManager.getSimpleMessage("not-enough-skill-tree-points", "point", "" + node.getSkillTreePointsConsumed()).send(player);
|
||||
MMOCore.plugin.soundManager.getSound(SoundEvent.NOT_ENOUGH_POINTS).playTo(getPlayer());
|
||||
event.setCancelled(true);
|
||||
return;
|
||||
}
|
||||
|
||||
}
|
@ -0,0 +1,7 @@
|
||||
package net.Indyuce.mmocore.gui.skilltree.display;
|
||||
|
||||
/**
|
||||
* The information needed to determine the display of a node/path depending on its context.
|
||||
*/
|
||||
public interface DisplayInfo {
|
||||
}
|
@ -1,4 +1,4 @@
|
||||
package net.Indyuce.mmocore.skilltree.tree.display;
|
||||
package net.Indyuce.mmocore.gui.skilltree.display;
|
||||
|
||||
import io.lumine.mythic.lib.UtilityMethods;
|
||||
import org.bukkit.Material;
|
||||
@ -23,7 +23,7 @@ public class Icon {
|
||||
|
||||
public Icon(ConfigurationSection config) {
|
||||
this(Material.valueOf(Objects.requireNonNull(UtilityMethods.enumName(
|
||||
config.getString("item")))), config.contains("model-data") ? config.getInt("model-data") : 0);
|
||||
config.getString("item")))), config.contains("custom-model-data") ? config.getInt("custom-model-data") : 0);
|
||||
}
|
||||
|
||||
public Icon(Material material, int customModelData) {
|
@ -0,0 +1,49 @@
|
||||
package net.Indyuce.mmocore.gui.skilltree.display;
|
||||
|
||||
import net.Indyuce.mmocore.skilltree.NodeStatus;
|
||||
|
||||
import java.util.Objects;
|
||||
|
||||
public class NodeDisplayInfo implements DisplayInfo{
|
||||
|
||||
private NodeStatus nodeStatus;
|
||||
private NodeType nodeType;
|
||||
|
||||
public NodeDisplayInfo(NodeType nodeType,NodeStatus nodeStatus) {
|
||||
this.nodeStatus = nodeStatus;
|
||||
this.nodeType = nodeType;
|
||||
}
|
||||
|
||||
public NodeStatus getNodeState() {
|
||||
return nodeStatus;
|
||||
}
|
||||
|
||||
public NodeStatus getNodeStatus() {
|
||||
return nodeStatus;
|
||||
}
|
||||
|
||||
public NodeType getNodeType() {
|
||||
return nodeType;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean equals(Object o) {
|
||||
if (this == o) return true;
|
||||
if (o == null || getClass() != o.getClass()) return false;
|
||||
NodeDisplayInfo that = (NodeDisplayInfo) o;
|
||||
return nodeStatus == that.nodeStatus && nodeType == that.nodeType;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int hashCode() {
|
||||
return Objects.hash(nodeStatus, nodeType);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return "NodeDisplayInfo{" +
|
||||
"nodeStatus=" + nodeStatus +
|
||||
", nodeType=" + nodeType +
|
||||
'}';
|
||||
}
|
||||
}
|
@ -0,0 +1,45 @@
|
||||
package net.Indyuce.mmocore.gui.skilltree.display;
|
||||
|
||||
public enum NodeType {
|
||||
UP_RIGHT_DOWN_LEFT,
|
||||
UP_RIGHT_DOWN,
|
||||
UP_RIGHT_LEFT,
|
||||
UP_DOWN_LEFT,
|
||||
DOWN_RIGHT_LEFT,
|
||||
UP_RIGHT,
|
||||
UP_DOWN,
|
||||
UP_LEFT,
|
||||
DOWN_RIGHT,
|
||||
DOWN_LEFT,
|
||||
RIGHT_LEFT,
|
||||
NO_PATH;
|
||||
|
||||
public static NodeType getNodeType(boolean hasUpPath, boolean hasRightPath, boolean hasDownPath, boolean hasLeftPath) {
|
||||
if (hasUpPath && hasRightPath && hasDownPath && hasLeftPath) {
|
||||
return UP_RIGHT_DOWN_LEFT;
|
||||
} else if (hasUpPath && hasRightPath && hasDownPath) {
|
||||
return UP_RIGHT_DOWN;
|
||||
} else if (hasUpPath && hasRightPath && hasLeftPath) {
|
||||
return UP_RIGHT_LEFT;
|
||||
} else if (hasUpPath && hasDownPath && hasLeftPath) {
|
||||
return UP_DOWN_LEFT;
|
||||
} else if (hasDownPath && hasRightPath && hasLeftPath) {
|
||||
return DOWN_RIGHT_LEFT;
|
||||
} else if (hasUpPath && hasRightPath) {
|
||||
return UP_RIGHT;
|
||||
} else if (hasUpPath && hasDownPath) {
|
||||
return UP_DOWN;
|
||||
} else if (hasUpPath && hasLeftPath) {
|
||||
return UP_LEFT;
|
||||
} else if (hasDownPath && hasRightPath) {
|
||||
return DOWN_RIGHT;
|
||||
} else if (hasDownPath && hasLeftPath) {
|
||||
return DOWN_LEFT;
|
||||
} else if (hasRightPath && hasLeftPath) {
|
||||
return RIGHT_LEFT;
|
||||
}
|
||||
return NO_PATH;
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -0,0 +1,44 @@
|
||||
package net.Indyuce.mmocore.gui.skilltree.display;
|
||||
|
||||
import java.util.Objects;
|
||||
|
||||
public class PathDisplayInfo implements DisplayInfo{
|
||||
|
||||
private PathType pathType;
|
||||
private PathStatus pathStatus;
|
||||
|
||||
public PathDisplayInfo(PathType pathType, PathStatus pathStatus) {
|
||||
this.pathType = pathType;
|
||||
this.pathStatus = pathStatus;
|
||||
}
|
||||
|
||||
public PathType getPathType() {
|
||||
return pathType;
|
||||
}
|
||||
|
||||
public PathStatus getPathStatus() {
|
||||
return pathStatus;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int hashCode() {
|
||||
return Objects.hash(pathType, pathStatus);
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public boolean equals(Object o) {
|
||||
if (this == o) return true;
|
||||
if (o == null || getClass() != o.getClass()) return false;
|
||||
PathDisplayInfo that = (PathDisplayInfo) o;
|
||||
return pathType == that.pathType && pathStatus == that.pathStatus;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return "PathDisplayInfo{" +
|
||||
"pathType=" + pathType +
|
||||
", pathStatus=" + pathStatus +
|
||||
'}';
|
||||
}
|
||||
}
|
@ -0,0 +1,7 @@
|
||||
package net.Indyuce.mmocore.gui.skilltree.display;
|
||||
|
||||
public enum PathStatus {
|
||||
LOCKED,
|
||||
FULLY_LOCKED,
|
||||
UNLOCKED;
|
||||
}
|
@ -0,0 +1,40 @@
|
||||
package net.Indyuce.mmocore.gui.skilltree.display;
|
||||
|
||||
/**
|
||||
* The direction of the path.
|
||||
*/
|
||||
public enum PathType {
|
||||
|
||||
UP,
|
||||
/**
|
||||
* Goes to up then east/ goes to left then down.
|
||||
*/
|
||||
UP_RIGHT,
|
||||
UP_LEFT,
|
||||
DOWN_RIGHT,
|
||||
DOWN_LEFT,
|
||||
RIGHT,
|
||||
|
||||
DEFAULT;
|
||||
|
||||
public static PathType getPathType(boolean hasUp,boolean hasRight,boolean hasDown,boolean hasLeft) {
|
||||
if ((hasUp || hasDown) && !hasLeft && !hasRight) {
|
||||
return UP;
|
||||
} else if ((hasRight || hasLeft)&& !hasUp && !hasDown) {
|
||||
return RIGHT;
|
||||
} else if (hasUp && hasRight) {
|
||||
return UP_RIGHT;
|
||||
}
|
||||
else if (hasUp && hasLeft) {
|
||||
return UP_LEFT;
|
||||
}
|
||||
else if (hasDown && hasRight) {
|
||||
return DOWN_RIGHT;
|
||||
}
|
||||
else if (hasDown && hasLeft) {
|
||||
return DOWN_LEFT;
|
||||
}
|
||||
return DEFAULT;
|
||||
}
|
||||
|
||||
}
|
@ -144,11 +144,10 @@ public class EditableFriendList extends EditableInventory {
|
||||
if (inv.getPlayerData().getFriends().size() <= n)
|
||||
return super.display(inv, n);
|
||||
|
||||
final OfflinePlayer friend = getEffectivePlayer(inv, n);
|
||||
final OfflinePlayer friend = Bukkit.getOfflinePlayer(inv.getPlayerData().getFriends().get(n));
|
||||
ItemStack disp = (friend.isOnline() ? online : offline).display(inv, n);
|
||||
ItemMeta meta = disp.getItemMeta();
|
||||
meta.getPersistentDataContainer().set(UUID_NAMESPACEDKEY, PersistentDataType.STRING, friend.getUniqueId().toString());
|
||||
|
||||
if (meta instanceof SkullMeta)
|
||||
inv.dynamicallyUpdateItem(this, n, disp, current -> {
|
||||
((SkullMeta) meta).setOwningPlayer(friend);
|
||||
|
@ -6,6 +6,7 @@ import java.util.logging.Level;
|
||||
import net.Indyuce.mmocore.MMOCore;
|
||||
import net.Indyuce.mmocore.api.player.profess.PlayerClass;
|
||||
import net.Indyuce.mmocore.api.util.MMOCoreUtils;
|
||||
import net.Indyuce.mmocore.gui.skilltree.SkillTreeViewer;
|
||||
import net.Indyuce.mmocore.gui.social.friend.EditableFriendList;
|
||||
import net.Indyuce.mmocore.gui.*;
|
||||
import net.Indyuce.mmocore.gui.social.friend.EditableFriendRemoval;
|
||||
|
@ -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() {
|
||||
|
@ -1,7 +1,6 @@
|
||||
package net.Indyuce.mmocore.skill;
|
||||
|
||||
import io.lumine.mythic.lib.MythicLib;
|
||||
import io.lumine.mythic.lib.api.player.MMOPlayerData;
|
||||
import io.lumine.mythic.lib.comp.flags.CustomFlag;
|
||||
import io.lumine.mythic.lib.player.cooldown.CooldownInfo;
|
||||
import io.lumine.mythic.lib.skill.Skill;
|
||||
@ -48,8 +47,8 @@ public class CastableSkill extends Skill {
|
||||
boolean loud = !getTrigger().isSilent();
|
||||
|
||||
// Skill is not usable yet
|
||||
if (!playerData.hasSkillUnlocked(skill)) {
|
||||
if (loud) MMOCore.plugin.configManager.getSimpleMessage("not-unlocked-skill").send(playerData.getPlayer());
|
||||
if (!playerData.hasUnlockedLevel(skill)) {
|
||||
if (loud) MMOCore.plugin.configManager.getSimpleMessage("skill-level-not-met").send(playerData.getPlayer());
|
||||
return false;
|
||||
}
|
||||
|
||||
|
@ -1,10 +1,12 @@
|
||||
package net.Indyuce.mmocore.skill;
|
||||
|
||||
import io.lumine.mythic.lib.MythicLib;
|
||||
import io.lumine.mythic.lib.api.player.EquipmentSlot;
|
||||
import io.lumine.mythic.lib.player.cooldown.CooldownObject;
|
||||
import io.lumine.mythic.lib.player.modifier.ModifierSource;
|
||||
import io.lumine.mythic.lib.player.skill.PassiveSkill;
|
||||
import io.lumine.mythic.lib.script.condition.Condition;
|
||||
import net.Indyuce.mmocore.MMOCore;
|
||||
import net.Indyuce.mmocore.api.player.PlayerData;
|
||||
import net.Indyuce.mmocore.api.util.math.formula.IntegerLinearValue;
|
||||
import net.Indyuce.mmocore.api.util.math.formula.LinearValue;
|
||||
@ -85,14 +87,18 @@ public class ClassSkill implements CooldownObject, Unlockable {
|
||||
@Override
|
||||
public void whenLocked(PlayerData playerData) {
|
||||
playerData.mapBoundSkills().forEach((slot, skill) -> {
|
||||
if (skill.equals(getUnlockNamespacedKey().split(":")[1]))
|
||||
if (skill.equalsIgnoreCase(getUnlockNamespacedKey().split(":")[1]))
|
||||
playerData.unbindSkill(slot);
|
||||
});
|
||||
//Update the stats to remove the passive skill if it is locked
|
||||
if (!MMOCore.plugin.configManager.passiveSkillNeedBound && getSkill().getTrigger().isPassive())
|
||||
playerData.getStats().updateStats();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void whenUnlocked(PlayerData playerData) {
|
||||
|
||||
if (!MMOCore.plugin.configManager.passiveSkillNeedBound && getSkill().getTrigger().isPassive())
|
||||
playerData.getStats().updateStats();
|
||||
}
|
||||
|
||||
/**
|
||||
@ -116,7 +122,7 @@ public class ClassSkill implements CooldownObject, Unlockable {
|
||||
|
||||
// Calculate placeholders
|
||||
Placeholders placeholders = new Placeholders();
|
||||
modifiers.keySet().forEach(modifier -> placeholders.register(modifier, data.getMMOPlayerData().getSkillModifierMap().getInstance(skill.getHandler(), modifier).getTotal(modifiers.get(modifier).calculate(x))));
|
||||
modifiers.keySet().forEach(modifier -> placeholders.register(modifier, MythicLib.plugin.getMMOConfig().decimal.format(data.getMMOPlayerData().getSkillModifierMap().getInstance(skill.getHandler(), modifier).getTotal(modifiers.get(modifier).calculate(x)))));
|
||||
placeholders.register("mana_name", data.getProfess().getManaDisplay().getName());
|
||||
placeholders.register("mana_color", data.getProfess().getManaDisplay().getFull().toString());
|
||||
|
||||
|
@ -121,7 +121,7 @@ public class RegisteredSkill {
|
||||
parsedExpression = parsedExpression.replace("<" + category + ">", "true");
|
||||
parsedExpression = parsedExpression.replaceAll("<.*?>", "false");
|
||||
try {
|
||||
return (boolean) MythicLib.plugin.getInterpreter().eval(parsedExpression);
|
||||
return (boolean) MythicLib.plugin.getFormulaParser().eval(parsedExpression);
|
||||
} catch (EvalError error) {
|
||||
throw new RuntimeException(error);
|
||||
}
|
||||
|
@ -64,8 +64,7 @@ public class BoundSkillInfo implements Closable {
|
||||
open = false;
|
||||
|
||||
// Unregister skill if passive
|
||||
if (isPassive())
|
||||
registered.unregister(playerData.getMMOPlayerData());
|
||||
if (isPassive()) registered.unregister(playerData.getMMOPlayerData());
|
||||
|
||||
// Remove skill buffs associated to the slot
|
||||
skillSlot.getSkillBuffTriggers().forEach(skillBuffTrigger -> skillBuffTrigger.remove(playerData, classSkill.getSkill().getHandler()));
|
||||
|
@ -126,11 +126,17 @@ public class SkillBar implements SkillCastingListener {
|
||||
}
|
||||
|
||||
private String getFormat(PlayerData data) {
|
||||
StringBuilder str = new StringBuilder();
|
||||
final StringBuilder str = new StringBuilder();
|
||||
if (!data.isOnline()) return str.toString();
|
||||
for (int slot : data.mapBoundSkills().keySet()) {
|
||||
ClassSkill skill = data.getBoundSkill(slot);
|
||||
str.append(str.isEmpty() ? "" : split).append((onCooldown(data, skill) ? onCooldown.replace("{cooldown}", String.valueOf(data.getCooldownMap().getInfo(skill).getRemaining() / 1000)) : noMana(data, skill) ? noMana : (noStamina(data, skill) ? noStamina : ready)).replace("{index}", String.valueOf(slot + (data.getPlayer().getInventory().getHeldItemSlot() < slot ? 1 : 0))).replace("{skill}", data.getBoundSkill(slot).getSkill().getName()));
|
||||
final ClassSkill skill = data.getBoundSkill(slot);
|
||||
if (skill.getSkill().getTrigger().isPassive()) continue;
|
||||
|
||||
str.append(str.isEmpty() ? "" : split).append((onCooldown(data, skill) ? onCooldown.replace("{cooldown}",
|
||||
String.valueOf(data.getCooldownMap().getInfo(skill).getRemaining() / 1000)) : noMana(data, skill) ? noMana : (noStamina(
|
||||
data, skill) ? noStamina : ready)).replace("{index}",
|
||||
String.valueOf(slot + (data.getPlayer().getInventory().getHeldItemSlot() < slot ? 1 : 0)))
|
||||
.replace("{skill}", data.getBoundSkill(slot).getSkill().getName()));
|
||||
}
|
||||
return MMOCore.plugin.placeholderParser.parse(data.getPlayer(), str.toString());
|
||||
}
|
||||
|
@ -3,5 +3,8 @@ package net.Indyuce.mmocore.skilltree;
|
||||
public enum ParentType {
|
||||
SOFT,
|
||||
|
||||
STRONG
|
||||
STRONG,
|
||||
|
||||
INCOMPATIBLE;
|
||||
|
||||
}
|
||||
|
@ -3,24 +3,35 @@ package net.Indyuce.mmocore.skilltree;
|
||||
import io.lumine.mythic.lib.MythicLib;
|
||||
import net.Indyuce.mmocore.MMOCore;
|
||||
import net.Indyuce.mmocore.api.player.PlayerData;
|
||||
import net.Indyuce.mmocore.api.util.MMOCoreUtils;
|
||||
import net.Indyuce.mmocore.experience.EXPSource;
|
||||
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.Bukkit;
|
||||
import org.bukkit.Location;
|
||||
import org.bukkit.configuration.ConfigurationSection;
|
||||
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 {
|
||||
private final SkillTree tree;
|
||||
private final String name, id;
|
||||
|
||||
private String permissionRequired;
|
||||
|
||||
private final Map<NodeStatus, Icon> icons = new HashMap<>();
|
||||
|
||||
private IntegerCoordinates coordinates;
|
||||
/**
|
||||
* The number of skill tree points this node requires.
|
||||
@ -45,8 +56,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
|
||||
@ -57,11 +67,22 @@ public class SkillTreeNode implements ExperienceObject {
|
||||
Validate.notNull(config, "Config cannot be null");
|
||||
this.id = config.getName();
|
||||
this.tree = tree;
|
||||
if (config.isConfigurationSection("display")) {
|
||||
for (NodeStatus status : NodeStatus.values()) {
|
||||
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()))));
|
||||
}
|
||||
}
|
||||
name = Objects.requireNonNull(config.getString("name"), "Could not find node name");
|
||||
size = Objects.requireNonNull(config.getInt("size"));
|
||||
isRoot = config.getBoolean("is-root", false);
|
||||
skillTreePointsConsumed=config.getInt("point-consumed",1);
|
||||
Validate.isTrue(skillTreePointsConsumed>0,"The skill tree points consumed by a node must be greater than 0.");
|
||||
skillTreePointsConsumed = config.getInt("point-consumed", 1);
|
||||
permissionRequired = config.getString("permission-required");
|
||||
Validate.isTrue(skillTreePointsConsumed > 0, "The skill tree points consumed by a node must be greater than 0.");
|
||||
if (config.contains("lores"))
|
||||
for (String key : config.getConfigurationSection("lores").getKeys(false))
|
||||
try {
|
||||
@ -84,6 +105,14 @@ public class SkillTreeNode implements ExperienceObject {
|
||||
return tree;
|
||||
}
|
||||
|
||||
public boolean hasIcon(NodeStatus status) {
|
||||
return icons.containsKey(status);
|
||||
}
|
||||
|
||||
public Icon getIcon(NodeStatus status) {
|
||||
return icons.get(status);
|
||||
}
|
||||
|
||||
public void setIsRoot() {
|
||||
isRoot = true;
|
||||
}
|
||||
@ -94,10 +123,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) {
|
||||
@ -112,12 +138,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() {
|
||||
@ -128,12 +165,16 @@ public class SkillTreeNode implements ExperienceObject {
|
||||
return maxChildren;
|
||||
}
|
||||
|
||||
public Set<SkillTreeNode> getSoftParents() {
|
||||
return softParents.keySet();
|
||||
public boolean hasPermissionRequirement(PlayerData playerData) {
|
||||
return permissionRequired == null || playerData.getPlayer().hasPermission(permissionRequired);
|
||||
}
|
||||
|
||||
public Set<SkillTreeNode> getStrongParents() {
|
||||
return strongParents.keySet();
|
||||
public Set<SkillTreeNode> getParents() {
|
||||
return parents.keySet().stream().map(ParentInformation::node).collect(Collectors.toSet());
|
||||
}
|
||||
|
||||
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() {
|
||||
@ -205,7 +246,7 @@ public class SkillTreeNode implements ExperienceObject {
|
||||
public Placeholders getPlaceholders(PlayerData playerData) {
|
||||
Placeholders holders = new Placeholders();
|
||||
holders.register("name", getName());
|
||||
holders.register("node-state", playerData.getNodeState(this));
|
||||
holders.register("node-state", playerData.getNodeStatus(this));
|
||||
holders.register("size", getSize());
|
||||
holders.register("level", playerData.getNodeLevel(this));
|
||||
holders.register("max-level", getMaxLevel());
|
||||
@ -226,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");
|
||||
}
|
||||
|
||||
|
@ -0,0 +1,17 @@
|
||||
package net.Indyuce.mmocore.skilltree;
|
||||
|
||||
import net.Indyuce.mmocore.api.player.PlayerData;
|
||||
import net.Indyuce.mmocore.gui.skilltree.display.PathStatus;
|
||||
import net.Indyuce.mmocore.skilltree.tree.SkillTree;
|
||||
|
||||
public record SkillTreePath(SkillTree tree, IntegerCoordinates coordinates, SkillTreeNode from, SkillTreeNode to) {
|
||||
|
||||
public PathStatus getStatus(PlayerData playerData) {
|
||||
if (playerData.getNodeStatus(from) == NodeStatus.UNLOCKED && playerData.getNodeStatus(to) == NodeStatus.UNLOCKED) {
|
||||
return PathStatus.UNLOCKED;
|
||||
}
|
||||
return PathStatus.LOCKED;
|
||||
}
|
||||
|
||||
|
||||
}
|
@ -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,20 +19,15 @@ 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 (config.isConfigurationSection("nodes." + node.getId() + ".parents"))
|
||||
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.SOFT);
|
||||
node.addParent(getNode(parent), section.getInt(parent), ParentType.valueOf(UtilityMethods.enumName(key)));
|
||||
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);
|
||||
}
|
||||
}
|
||||
}
|
||||
setupRoots();
|
||||
@ -41,7 +37,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();
|
||||
|
@ -1,129 +0,0 @@
|
||||
package net.Indyuce.mmocore.skilltree.tree;
|
||||
|
||||
import net.Indyuce.mmocore.api.player.PlayerData;
|
||||
import net.Indyuce.mmocore.skilltree.IntegerCoordinates;
|
||||
import net.Indyuce.mmocore.skilltree.NodeStatus;
|
||||
import net.Indyuce.mmocore.skilltree.ParentType;
|
||||
import net.Indyuce.mmocore.skilltree.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;
|
||||
|
||||
/**
|
||||
* For linked skillTrees there is no notion of children and
|
||||
* parents you must have some neighbours unlocked in order to
|
||||
* be unlockable. All the relationships in the tree are
|
||||
* defined by the coordinates you nodes have.
|
||||
*/
|
||||
public class LinkedSkillTree extends SkillTree {
|
||||
public LinkedSkillTree(ConfigurationSection config) {
|
||||
super(config);
|
||||
|
||||
// Setup the coordinate map because coordinates are given in the yml for linked skill tree
|
||||
coordinatesSetup();
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void whenPostLoaded(@NotNull ConfigurationSection config) {
|
||||
// Setup the children and parents if a node precise a required level for upgrade.
|
||||
// If it is not filled the algorithm will put the required level to 1
|
||||
for (SkillTreeNode node : nodes.values()) {
|
||||
if (config.contains(node.getId() + ".children")) {
|
||||
ConfigurationSection section = config.getConfigurationSection(node.getId() + ".children.soft");
|
||||
if (section != null) {
|
||||
for (String child : section.getKeys(false)) {
|
||||
node.addChild(getNode(child));
|
||||
getNode(child).addParent(node, section.getInt(child), ParentType.SOFT);
|
||||
}
|
||||
}
|
||||
section = config.getConfigurationSection(node.getId() + ".children.strong");
|
||||
if (section != null) {
|
||||
for (String child : section.getKeys(false)) {
|
||||
node.addChild(getNode(child));
|
||||
getNode(child).addParent(node, section.getInt(child), ParentType.STRONG);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
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) ");
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setupNodeStates(PlayerData playerData) {
|
||||
|
||||
// Values are labelled as unlockable
|
||||
for (SkillTreeNode root : roots)
|
||||
playerData.setNodeState(root, NodeStatus.UNLOCKABLE);
|
||||
|
||||
// All the nodes with level >0 are unlocked
|
||||
for (SkillTreeNode node : nodes.values()) {
|
||||
if (playerData.getNodeLevel(node) > 0)
|
||||
playerData.setNodeState(node, NodeStatus.UNLOCKED);
|
||||
}
|
||||
// Setup unlockable nodes
|
||||
for (SkillTreeNode node : nodes.values()) {
|
||||
if (isUnlockable(node, playerData) && !playerData.hasNodeState(node))
|
||||
playerData.setNodeState(node, NodeStatus.UNLOCKABLE);
|
||||
}
|
||||
|
||||
labelLockedNodes(playerData);
|
||||
|
||||
// Label all the remaining nodes to FULLY LOCKED
|
||||
for (SkillTreeNode node : nodes.values())
|
||||
if (!playerData.hasNodeState(node))
|
||||
playerData.setNodeState(node, NodeStatus.FULLY_LOCKED);
|
||||
}
|
||||
|
||||
/**
|
||||
* We recursively label all the locked nodes who are connected to an unlockable node.
|
||||
**/
|
||||
private void labelLockedNodes(PlayerData playerData) {
|
||||
List<SkillTreeNode> unlockableNodes = nodes.values().stream().filter(node -> playerData.getNodeState(node) == NodeStatus.UNLOCKABLE).toList();
|
||||
for (SkillTreeNode node : unlockableNodes) {
|
||||
labelLockedNodesFrom(playerData, node);
|
||||
}
|
||||
}
|
||||
|
||||
private void labelLockedNodesFrom(PlayerData data, SkillTreeNode node) {
|
||||
for (IntegerCoordinates coor : getCheckCoordinates(node.getCoordinates())) {
|
||||
if (isNode(coor) && !data.hasNodeState(getNode(coor))) {
|
||||
data.setNodeState(getNode(coor), NodeStatus.LOCKED);
|
||||
labelLockedNodesFrom(data, getNode(coor));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private List<IntegerCoordinates> getCheckCoordinates(IntegerCoordinates coor) {
|
||||
return Arrays.asList(new IntegerCoordinates(coor.getX() + 1, coor.getY()),
|
||||
new IntegerCoordinates(coor.getX() - 1, coor.getY()), new IntegerCoordinates(coor.getX(), coor.getY() + 1), new IntegerCoordinates(coor.getX(), coor.getY() - 1));
|
||||
}
|
||||
|
||||
private boolean isUnlockable(SkillTreeNode node, PlayerData playerData) {
|
||||
|
||||
boolean isUnlockable = false;
|
||||
for (IntegerCoordinates coordinates : getCheckCoordinates(node.getCoordinates())) {
|
||||
if (isNode(coordinates))
|
||||
if (isNode(coordinates) && playerData.getNodeState(getNode(coordinates)) == NodeStatus.UNLOCKED && countUnlockedNeighbours(coordinates, playerData) <= getNode(coordinates).getMaxChildren())
|
||||
isUnlockable = true;
|
||||
}
|
||||
return isUnlockable;
|
||||
}
|
||||
|
||||
/**
|
||||
* Counts the number of unlocked neighbours of a node for a certain playerData
|
||||
**/
|
||||
private int countUnlockedNeighbours(IntegerCoordinates coor, PlayerData playerData) {
|
||||
int number = 0;
|
||||
for (IntegerCoordinates coordinates : getCheckCoordinates(coor)) {
|
||||
if (isNode(coordinates) && playerData.getNodeLevel(getNode(coordinates)) > 0)
|
||||
number++;
|
||||
}
|
||||
return number;
|
||||
}
|
||||
}
|
@ -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) {
|
||||
}
|
@ -6,11 +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.tree.display.DisplayInfo;
|
||||
import net.Indyuce.mmocore.skilltree.tree.display.Icon;
|
||||
import net.Indyuce.mmocore.skilltree.IntegerCoordinates;
|
||||
import net.Indyuce.mmocore.skilltree.NodeStatus;
|
||||
import net.Indyuce.mmocore.skilltree.SkillTreeNode;
|
||||
import net.Indyuce.mmocore.skilltree.*;
|
||||
import org.apache.commons.lang.Validate;
|
||||
import org.bukkit.Material;
|
||||
import org.bukkit.configuration.ConfigurationSection;
|
||||
@ -18,7 +14,6 @@ import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
import java.util.*;
|
||||
import java.util.logging.Level;
|
||||
|
||||
/**
|
||||
* A passive skill tree that features nodes, or passive skills.
|
||||
@ -42,16 +37,20 @@ public abstract class SkillTree extends PostLoadObject implements RegisteredObje
|
||||
private final int customModelData;
|
||||
|
||||
//2 different maps to get the nodes
|
||||
|
||||
//Represents all the coordinates that will be displayed as a path (between 2 nodes of the tree)
|
||||
protected final ArrayList<IntegerCoordinates> paths = new ArrayList<>();
|
||||
//Represents all the nodes
|
||||
/**
|
||||
* Represents all the nodes
|
||||
* Key: the coordinates of the node
|
||||
* Value: the node
|
||||
*/
|
||||
protected final Map<IntegerCoordinates, SkillTreeNode> coordinatesNodes = new HashMap<>();
|
||||
/**
|
||||
* Represents all the paths between nodes.
|
||||
*/
|
||||
protected final Map<IntegerCoordinates, SkillTreePath> coordinatesPaths = new HashMap<>();
|
||||
|
||||
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 HashMap<DisplayInfo, Icon> icons = new HashMap<>();
|
||||
protected final List<SkillTreeNode> roots = new ArrayList<>();
|
||||
|
||||
public SkillTree(ConfigurationSection config) {
|
||||
@ -66,47 +65,31 @@ public abstract class SkillTree extends PostLoadObject implements RegisteredObje
|
||||
this.maxPointSpent = config.getInt("max-point-spent", Integer.MAX_VALUE);
|
||||
for (String key : config.getConfigurationSection("nodes").getKeys(false)) {
|
||||
try {
|
||||
|
||||
SkillTreeNode node = new SkillTreeNode(this, config.getConfigurationSection("nodes." + key));
|
||||
ConfigurationSection section = config.getConfigurationSection("nodes." + key);
|
||||
SkillTreeNode node = new SkillTreeNode(this, section);
|
||||
nodes.put(node.getId(), node);
|
||||
|
||||
} catch (Exception e) {
|
||||
MMOCore.log("Couldn't load skill tree node " + id + "." + key + ": " + e.getMessage());
|
||||
}
|
||||
}
|
||||
try {
|
||||
if (config.contains("paths")) {
|
||||
ConfigurationSection section = config.getConfigurationSection("paths");
|
||||
for (String key : section.getKeys(false)) {
|
||||
if (section.contains(key + ".x") && section.contains(key + ".y")) {
|
||||
paths.add(new IntegerCoordinates(section.getInt(key + ".x"), section.getInt(key + ".y")));
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
} catch (Exception e) {
|
||||
MMOCore.log(Level.WARNING, "Couldn't load paths for skill tree: " + id);
|
||||
}
|
||||
|
||||
try {
|
||||
//Load the icons of the skill tree.
|
||||
for (String key : config.getConfigurationSection("icons").getKeys(false)) {
|
||||
if (key.equalsIgnoreCase("path")) {
|
||||
icons.put(DisplayInfo.pathInfo, new Icon(config.getConfigurationSection("icons." + key)));
|
||||
for (String from : config.getConfigurationSection("nodes").getKeys(false)) {
|
||||
ConfigurationSection section = config.getConfigurationSection("nodes." + from);
|
||||
if (section.contains("paths")) {
|
||||
for (String to : section.getConfigurationSection("paths").getKeys(false)) {
|
||||
SkillTreeNode node1 = nodes.get(to);
|
||||
if (node1 == null) {
|
||||
MMOCore.log("Couldn't find node " + to + " for path in node " + from + ".");
|
||||
continue;
|
||||
}
|
||||
for (String size : config.getConfigurationSection("icons." + key).getKeys(false)) {
|
||||
DisplayInfo displayInfo = new DisplayInfo(NodeStatus.valueOf(UtilityMethods.enumName(key)), Integer.parseInt(size));
|
||||
Icon icon = new Icon(config.getConfigurationSection("icons." + key + "." + size));
|
||||
icons.put(displayInfo, icon);
|
||||
for (String pathKey : section.getConfigurationSection("paths." + to).getKeys(false)) {
|
||||
IntegerCoordinates coordinates = new IntegerCoordinates(section.getInt("paths." + to + "." + pathKey + ".x"), section.getInt("paths." + to + "." + pathKey + ".y"));
|
||||
coordinatesPaths.put(coordinates, new SkillTreePath(this, coordinates, nodes.get(from), node1));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
} catch (Exception e) {
|
||||
MMOCore.log("Couldn't load icons for the skill tree " + id);
|
||||
e.printStackTrace();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Used to setup everything related to coordinates when each node has its coordinates loaded.
|
||||
@ -122,27 +105,6 @@ public abstract class SkillTree extends PostLoadObject implements RegisteredObje
|
||||
@Override
|
||||
protected abstract void whenPostLoaded(@NotNull ConfigurationSection configurationSection);
|
||||
|
||||
public Icon getIcon(DisplayInfo info) {
|
||||
Validate.isTrue(icons.containsKey(info), "The icon corresponding to " + info + " doesn't exist for the skill tree " + id + ".");
|
||||
return icons.get(info);
|
||||
}
|
||||
|
||||
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;
|
||||
}
|
||||
@ -159,19 +121,8 @@ public abstract class SkillTree extends PostLoadObject implements RegisteredObje
|
||||
SkillTree skillTree = null;
|
||||
|
||||
try {
|
||||
String string = config.getString("type");
|
||||
Validate.notNull(string, "You must precise a type for the skill tree.");
|
||||
Validate.isTrue(string.equals("linked") || string.equals("custom"), "You must precise the type of the skill tree in the yml!" +
|
||||
"\nAllowed values: 'linked','custom'");
|
||||
|
||||
if (string.equals("linked")) {
|
||||
skillTree = new LinkedSkillTree(config);
|
||||
skillTree.postLoad();
|
||||
}
|
||||
if (string.equals("custom")) {
|
||||
skillTree = new CustomSkillTree(config);
|
||||
skillTree.postLoad();
|
||||
}
|
||||
} catch (Exception e) {
|
||||
MMOCore.log("Couldn't load skill tree " + config.getString("id") + ": " + e.getMessage());
|
||||
}
|
||||
@ -204,13 +155,19 @@ 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
|
||||
@ -220,25 +177,31 @@ public abstract class SkillTree extends PostLoadObject implements RegisteredObje
|
||||
numberChildren++;
|
||||
|
||||
//We must check if the parent is Fully Locked or not and if it can unlock a new node(with its max children constraint)
|
||||
if (numberChildren >= strongParent.getMaxChildren() || playerData.getNodeState(strongParent) == NodeStatus.FULLY_LOCKED)
|
||||
if (numberChildren >= strongParent.getMaxChildren() || playerData.getNodeStatus(strongParent) == NodeStatus.FULLY_LOCKED)
|
||||
isFullyLockedFromStrongParent = true;
|
||||
}
|
||||
|
||||
|
||||
for (SkillTreeNode softParent : node.getSoftParents()) {
|
||||
if (playerData.getNodeLevel(softParent) >= node.getParentNeededLevel(softParent)) {
|
||||
for (SkillTreeNode softParent : softParents) {
|
||||
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)
|
||||
numberChildren++;
|
||||
if (numberChildren < softParent.getMaxChildren() && playerData.getNodeState(softParent) != NodeStatus.FULLY_LOCKED)
|
||||
if (numberChildren < softParent.getMaxChildren() && playerData.getNodeStatus(softParent) != NodeStatus.FULLY_LOCKED)
|
||||
isFullyLockedFromSoftParent = false;
|
||||
}
|
||||
for (SkillTreeNode incompatibleParent : incompatibleParents) {
|
||||
if (playerData.getNodeLevel(incompatibleParent) >= node.getParentNeededLevel(incompatibleParent, ParentType.INCOMPATIBLE)) {
|
||||
isFullyLockedFromIncompatibleParent = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
boolean isFullyLocked = isFullyLockedFromSoftParent || isFullyLockedFromStrongParent;
|
||||
boolean isFullyLocked = isFullyLockedFromSoftParent || isFullyLockedFromStrongParent || isFullyLockedFromIncompatibleParent;
|
||||
boolean isUnlockable = isUnlockableFromSoftParent && isUnlockableFromStrongParent;
|
||||
if (isFullyLocked)
|
||||
playerData.setNodeState(node, NodeStatus.FULLY_LOCKED);
|
||||
@ -266,7 +229,7 @@ public abstract class SkillTree extends PostLoadObject implements RegisteredObje
|
||||
}
|
||||
|
||||
public boolean isPath(IntegerCoordinates coordinates) {
|
||||
return paths.contains(coordinates);
|
||||
return coordinatesPaths.keySet().contains(coordinates);
|
||||
}
|
||||
|
||||
public Material getItem() {
|
||||
@ -291,11 +254,17 @@ public abstract class SkillTree extends PostLoadObject implements RegisteredObje
|
||||
return Objects.requireNonNull(coordinatesNodes.get(coords), "Could not find node in tree '" + id + "' with coordinates '" + coords.toString() + "'");
|
||||
}
|
||||
|
||||
@NotNull
|
||||
public SkillTreePath getPath(IntegerCoordinates coords) {
|
||||
return Objects.requireNonNull(coordinatesPaths.get(coords), "Could not find path 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 + "'");
|
||||
}
|
||||
|
||||
|
||||
public boolean isNode(String name) {
|
||||
return nodes.containsKey(name);
|
||||
}
|
||||
|
@ -1,57 +0,0 @@
|
||||
package net.Indyuce.mmocore.skilltree.tree.display;
|
||||
|
||||
import net.Indyuce.mmocore.skilltree.NodeStatus;
|
||||
|
||||
import java.util.Objects;
|
||||
|
||||
/**
|
||||
* The information needed to determine the display type of a node
|
||||
*/
|
||||
public class DisplayInfo {
|
||||
private NodeStatus nodeStatus;
|
||||
private int size;
|
||||
|
||||
//this NodeDisplayInfo represent a path
|
||||
public static DisplayInfo pathInfo= new DisplayInfo();
|
||||
|
||||
|
||||
public DisplayInfo() {
|
||||
}
|
||||
|
||||
public DisplayInfo(NodeStatus nodeStatus, int size) {
|
||||
this.nodeStatus = nodeStatus;
|
||||
this.size = size;
|
||||
}
|
||||
|
||||
public NodeStatus getNodeState() {
|
||||
return nodeStatus;
|
||||
}
|
||||
|
||||
public int getSize() {
|
||||
return size;
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public int hashCode() {
|
||||
return Objects.hash(nodeStatus, size);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean equals(Object obj) {
|
||||
if(!(obj instanceof DisplayInfo))
|
||||
return false;
|
||||
DisplayInfo displayInfo= (DisplayInfo) obj;
|
||||
if(nodeStatus ==null)
|
||||
return displayInfo.getNodeState()==null;
|
||||
return nodeStatus ==displayInfo.getNodeState()&&size==displayInfo.getSize();
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return "DisplayInfo{" +
|
||||
"nodeState=" + nodeStatus +
|
||||
", size=" + size +
|
||||
'}';
|
||||
}
|
||||
}
|
@ -21,13 +21,6 @@ public class MMOCoreBukkit {
|
||||
if (plugin.configManager.overrideVanillaExp)
|
||||
Bukkit.getPluginManager().registerEvents(new VanillaExperienceOverride(), plugin);
|
||||
|
||||
if (plugin.getConfig().getBoolean("hotbar-swapping.enabled"))
|
||||
try {
|
||||
Bukkit.getPluginManager().registerEvents(new HotbarSwap(plugin.getConfig().getConfigurationSection("hotbar-swapping")), plugin);
|
||||
} catch (RuntimeException exception) {
|
||||
plugin.getLogger().log(Level.WARNING, "Could not load hotbar swapping: " + exception.getMessage());
|
||||
}
|
||||
|
||||
if (plugin.getConfig().getBoolean("prevent-spawner-xp"))
|
||||
Bukkit.getPluginManager().registerEvents(new NoSpawnerEXP(), plugin);
|
||||
|
||||
|
@ -1,41 +0,0 @@
|
||||
package net.Indyuce.mmocore.listener.option;
|
||||
|
||||
import io.lumine.mythic.lib.UtilityMethods;
|
||||
import net.Indyuce.mmocore.MMOCore;
|
||||
import net.Indyuce.mmocore.api.event.PlayerKeyPressEvent;
|
||||
import net.Indyuce.mmocore.api.SoundEvent;
|
||||
import net.Indyuce.mmocore.skill.cast.PlayerKey;
|
||||
import org.bukkit.configuration.ConfigurationSection;
|
||||
import org.bukkit.entity.Player;
|
||||
import org.bukkit.event.EventHandler;
|
||||
import org.bukkit.event.Listener;
|
||||
import org.bukkit.inventory.ItemStack;
|
||||
|
||||
import java.util.Objects;
|
||||
|
||||
public class HotbarSwap implements Listener {
|
||||
private final PlayerKey keybind;
|
||||
private final boolean crouching;
|
||||
|
||||
public HotbarSwap(ConfigurationSection config) {
|
||||
this.keybind = PlayerKey.valueOf(UtilityMethods.enumName(Objects.requireNonNull(config.getString("keybind"), "Could not find keybind")));
|
||||
this.crouching = config.getBoolean("crouching");
|
||||
}
|
||||
|
||||
@EventHandler
|
||||
public void keyPress(PlayerKeyPressEvent event) {
|
||||
Player player = event.getPlayer();
|
||||
if (event.getPressed() == keybind && event.getPlayer().isSneaking() == crouching) {
|
||||
|
||||
if (event.getPressed().shouldCancelEvent())
|
||||
event.setCancelled(true);
|
||||
|
||||
MMOCore.plugin.soundManager.getSound(SoundEvent.HOTBAR_SWAP).playTo(player);
|
||||
for (int j = 0; j < 9; j++) {
|
||||
ItemStack replaced = player.getInventory().getItem(j + 9 * 3);
|
||||
player.getInventory().setItem(j + 9 * 3, player.getInventory().getItem(j));
|
||||
player.getInventory().setItem(j, replaced);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
@ -16,7 +16,7 @@ mysql:
|
||||
port: 3306
|
||||
database: minecraft
|
||||
user: mmolover
|
||||
pass: ILoveAria
|
||||
pass: ILovePhoenixDev
|
||||
properties:
|
||||
cachePrepStmts: true
|
||||
prepStmtCacheSize: 250
|
||||
@ -223,20 +223,6 @@ max-skill-slots: 8
|
||||
# Disabled for now.
|
||||
# passive-skill-need-bound: true
|
||||
|
||||
# Fun extra RPG feature that switches the player's hotbar with
|
||||
# the 9 lower row items of his inventory. This allows the player
|
||||
# to have two different item sets or quickly have access to pots
|
||||
# Requires a SERVER reload when changed.
|
||||
hotbar-swapping:
|
||||
enabled: true
|
||||
|
||||
# Keybind which triggers
|
||||
# Available keybinds
|
||||
keybind: SWAP_HANDS
|
||||
|
||||
# If the player has to sneak to swap hotbars
|
||||
crouching: true
|
||||
|
||||
# Set this to true to allow players in
|
||||
# creative mode to enter casting mode
|
||||
can-creative-cast: false
|
||||
|
@ -56,6 +56,8 @@ items:
|
||||
lore:
|
||||
- '&7Current Skill: &6{skill}'
|
||||
- ''
|
||||
- '{slot-lore}'
|
||||
- ''
|
||||
- '&7&oCast this spell by pressing [F] followed'
|
||||
- '&7&oby the keybind displayed on the action bar.'
|
||||
- ''
|
||||
|
@ -99,4 +99,221 @@ items:
|
||||
- '&e◆Points spent for {skill-tree}&e:&6 {point-spent}'
|
||||
- '&e◆Current {skill-tree} &epoints: &6{skill-tree-points}'
|
||||
- '&e◆Current &6global&e points: &6{global-points}'
|
||||
display:
|
||||
paths:
|
||||
unlocked:
|
||||
up:
|
||||
item: "WHITE_DYE"
|
||||
custom-model-data: 0
|
||||
up-right:
|
||||
item: "WHITE_DYE"
|
||||
custom-model-data: 0
|
||||
up-left:
|
||||
item: "WHITE_DYE"
|
||||
custom-model-data: 0
|
||||
down-right:
|
||||
item: "WHITE_DYE"
|
||||
custom-model-data: 0
|
||||
down-left:
|
||||
item: "WHITE_DYE"
|
||||
custom-model-data: 0
|
||||
right:
|
||||
item: "WHITE_DYE"
|
||||
custom-model-data: 0
|
||||
default:
|
||||
item: "WHITE_DYE"
|
||||
custom-model-data: 0
|
||||
locked:
|
||||
up:
|
||||
item: "GRAY_DYE"
|
||||
custom-model-data: 0
|
||||
up-right:
|
||||
item: "GRAY_DYE"
|
||||
custom-model-data: 0
|
||||
up-left:
|
||||
item: "GRAY_DYE"
|
||||
custom-model-data: 0
|
||||
down-right:
|
||||
item: "GRAY_DYE"
|
||||
custom-model-data: 0
|
||||
down-left:
|
||||
item: "GRAY_DYE"
|
||||
custom-model-data: 0
|
||||
right:
|
||||
item: "GRAY_DYE"
|
||||
custom-model-data: 0
|
||||
default:
|
||||
item: "GRAY_DYE"
|
||||
custom-model-data: 0
|
||||
fully-locked:
|
||||
up:
|
||||
item: "BLACK_DYE"
|
||||
custom-model-data: 0
|
||||
up-right:
|
||||
item: "BLACK_DYE"
|
||||
custom-model-data: 0
|
||||
up-left:
|
||||
item: "BLACK_DYE"
|
||||
custom-model-data: 0
|
||||
down-right:
|
||||
item: "BLACK_DYE"
|
||||
custom-model-data: 0
|
||||
down-left:
|
||||
item: "BLACK_DYE"
|
||||
custom-model-data: 0
|
||||
right:
|
||||
item: "BLACK_DYE"
|
||||
custom-model-data: 0
|
||||
default:
|
||||
item: "BLACK_DYE"
|
||||
custom-model-data: 0
|
||||
nodes:
|
||||
unlocked:
|
||||
up-right-down-left:
|
||||
item: "WHITE_CONCRETE"
|
||||
custom-model-data: 0
|
||||
up-right-down:
|
||||
item: "WHITE_CONCRETE"
|
||||
custom-model-data: 0
|
||||
up-right-left:
|
||||
item: "WHITE_CONCRETE"
|
||||
custom-model-data: 0
|
||||
up-down-left:
|
||||
item: "WHITE_CONCRETE"
|
||||
custom-model-data: 0
|
||||
down-right-left:
|
||||
item: "WHITE_CONCRETE"
|
||||
custom-model-data: 0
|
||||
up-right:
|
||||
item: "WHITE_CONCRETE"
|
||||
custom-model-data: 0
|
||||
up-down:
|
||||
item: "WHITE_CONCRETE"
|
||||
custom-model-data: 0
|
||||
up-left:
|
||||
item: "WHITE_CONCRETE"
|
||||
custom-model-data: 0
|
||||
down-right:
|
||||
item: "WHITE_CONCRETE"
|
||||
custom-model-data: 0
|
||||
down-left:
|
||||
item: "WHITE_CONCRETE"
|
||||
custom-model-data: 0
|
||||
right-left:
|
||||
item: "WHITE_CONCRETE"
|
||||
custom-model-data: 0
|
||||
no-path:
|
||||
item: "WHITE_CONCRETE"
|
||||
custom-model-data: 0
|
||||
locked:
|
||||
up-right-down-left:
|
||||
item: "GRAY_CONCRETE"
|
||||
custom-model-data: 0
|
||||
up-right-down:
|
||||
item: "GRAY_CONCRETE"
|
||||
custom-model-data: 0
|
||||
up-right-left:
|
||||
item: "GRAY_CONCRETE"
|
||||
custom-model-data: 0
|
||||
up-down-left:
|
||||
item: "GRAY_CONCRETE"
|
||||
custom-model-data: 0
|
||||
down-right-left:
|
||||
item: "GRAY_CONCRETE"
|
||||
custom-model-data: 0
|
||||
up-right:
|
||||
item: "GRAY_CONCRETE"
|
||||
custom-model-data: 0
|
||||
up-down:
|
||||
item: "GRAY_CONCRETE"
|
||||
custom-model-data: 0
|
||||
up-left:
|
||||
item: "GRAY_CONCRETE"
|
||||
custom-model-data: 0
|
||||
down-right:
|
||||
item: "GRAY_CONCRETE"
|
||||
custom-model-data: 0
|
||||
down-left:
|
||||
item: "GRAY_CONCRETE"
|
||||
custom-model-data: 0
|
||||
right-left:
|
||||
item: "GRAY_CONCRETE"
|
||||
custom-model-data: 0
|
||||
no-path:
|
||||
item: "GRAY_CONCRETE"
|
||||
custom-model-data: 0
|
||||
unlockable:
|
||||
up-right-down-left:
|
||||
item: "BLUE_CONCRETE"
|
||||
custom-model-data: 0
|
||||
up-right-down:
|
||||
item: "BLUE_CONCRETE"
|
||||
custom-model-data: 0
|
||||
up-right-left:
|
||||
item: "BLUE_CONCRETE"
|
||||
custom-model-data: 0
|
||||
up-down-left:
|
||||
item: "BLUE_CONCRETE"
|
||||
custom-model-data: 0
|
||||
down-right-left:
|
||||
item: "BLUE_CONCRETE"
|
||||
custom-model-data: 0
|
||||
up-right:
|
||||
item: "BLUE_CONCRETE"
|
||||
custom-model-data: 0
|
||||
up-down:
|
||||
item: "BLUE_CONCRETE"
|
||||
custom-model-data: 0
|
||||
up-left:
|
||||
item: "BLUE_CONCRETE"
|
||||
custom-model-data: 0
|
||||
down-right:
|
||||
item: "BLUE_CONCRETE"
|
||||
custom-model-data: 0
|
||||
down-left:
|
||||
item: "BLUE_CONCRETE"
|
||||
custom-model-data: 0
|
||||
right-left:
|
||||
item: "BLUE_CONCRETE"
|
||||
custom-model-data: 0
|
||||
no-path:
|
||||
item: "BLUE_CONCRETE"
|
||||
custom-model-data: 0
|
||||
fully-locked:
|
||||
up-right-down-left:
|
||||
item: "BLACK_CONCRETE"
|
||||
custom-model-data: 0
|
||||
up-right-down:
|
||||
item: "BLACK_CONCRETE"
|
||||
custom-model-data: 0
|
||||
up-right-left:
|
||||
item: "BLACK_CONCRETE"
|
||||
custom-model-data: 0
|
||||
up-down-left:
|
||||
item: "BLACK_CONCRETE"
|
||||
custom-model-data: 0
|
||||
down-right-left:
|
||||
item: "BLACK_CONCRETE"
|
||||
custom-model-data: 0
|
||||
up-right:
|
||||
item: "BLACK_CONCRETE"
|
||||
custom-model-data: 0
|
||||
up-down:
|
||||
item: "BLACK_CONCRETE"
|
||||
custom-model-data: 0
|
||||
up-left:
|
||||
item: "BLACK_CONCRETE"
|
||||
custom-model-data: 0
|
||||
down-right:
|
||||
item: "BLACK_CONCRETE"
|
||||
custom-model-data: 0
|
||||
down-left:
|
||||
item: "BLACK_CONCRETE"
|
||||
custom-model-data: 0
|
||||
right-left:
|
||||
item: "BLACK_CONCRETE"
|
||||
custom-model-data: 0
|
||||
no-path:
|
||||
item: "BLACK_CONCRETE"
|
||||
custom-model-data: 0
|
||||
|
||||
|
@ -216,6 +216,7 @@ no-skill-tree-points-spent: '&cYou have not spent any skill tree points.'
|
||||
locked-node: '&cThis skill is locked!'
|
||||
upgrade-skill-node: '&eYour skill node &6{skill-node} &eis now Level &6{level}&e!'
|
||||
skill-node-max-level-hit: '&cYou already hit the max level for that skill node.'
|
||||
missing-skill-node-permission: '&cYou do not have the permission to unlock this skill node.'
|
||||
not-enough-skill-tree-points: '&cYou need {point} skill tree point.'
|
||||
reallocated-points: '&eYou successfully reset the skill tree {skill-tree}. &eYou now have &6{points} &eskill tree points.'
|
||||
not-skill-tree-reallocation-point: '&cYou do not have 1 skill tree reallocation point.'
|
||||
|
Loading…
Reference in New Issue
Block a user