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:
Jules 2023-05-15 00:06:02 +02:00
commit ac36e3eaa1
39 changed files with 749 additions and 482 deletions

View File

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

View File

@ -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

View File

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

View File

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

View File

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

View File

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

View File

@ -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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@ -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 +
'}';
}
}

View File

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

View File

@ -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 +
'}';
}
}

View File

@ -0,0 +1,7 @@
package net.Indyuce.mmocore.gui.skilltree.display;
public enum PathStatus {
LOCKED,
FULLY_LOCKED,
UNLOCKED;
}

View File

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

View File

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

View File

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

View File

@ -1,6 +1,7 @@
package net.Indyuce.mmocore.manager;
import net.Indyuce.mmocore.MMOCore;
import net.Indyuce.mmocore.skilltree.ParentType;
import net.Indyuce.mmocore.skilltree.tree.SkillTree;
import net.Indyuce.mmocore.manager.registry.MMOCoreRegister;
import net.Indyuce.mmocore.skilltree.SkillTreeNode;
@ -35,7 +36,7 @@ public class SkillTreeManager extends MMOCoreRegister<SkillTree> {
* @return The list of all the roots (e.g the nodes without any parents
*/
public List<SkillTreeNode> getRootNodes() {
return skillTreeNodes.values().stream().filter(treeNode -> treeNode.getSoftParents().size() == 0).collect(Collectors.toList());
return skillTreeNodes.values().stream().filter(treeNode -> treeNode.getParents(ParentType.SOFT).size() == 0).collect(Collectors.toList());
}
public Collection<SkillTreeNode> getAllNodes() {

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@ -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() {
@ -153,7 +194,7 @@ public class SkillTreeNode implements ExperienceObject {
/**
* @return Full node identifier, containing both the node identifier AND
* the skill tree identifier, like "combat_extra_strength"
* the skill tree identifier, like "combat_extra_strength"
*/
public String getFullId() {
return tree.getId() + "_" + id;
@ -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");
}

View File

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

View File

@ -1,5 +1,6 @@
package net.Indyuce.mmocore.skilltree.tree;
import io.lumine.mythic.lib.UtilityMethods;
import net.Indyuce.mmocore.skilltree.ParentType;
import net.Indyuce.mmocore.skilltree.SkillTreeNode;
import org.bukkit.configuration.ConfigurationSection;
@ -18,21 +19,16 @@ public class CustomSkillTree extends SkillTree {
// Setup the children and parents for each node.
for (SkillTreeNode node : nodes.values()) {
ConfigurationSection section = config.getConfigurationSection("nodes." + node.getId() + ".parents.soft");
if (section != null) {
for (String parent : section.getKeys(false)) {
node.addParent(getNode(parent), section.getInt(parent), ParentType.SOFT);
getNode(parent).addChild(node);
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.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();

View File

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

View File

@ -0,0 +1,7 @@
package net.Indyuce.mmocore.skilltree.tree;
import net.Indyuce.mmocore.skilltree.ParentType;
import net.Indyuce.mmocore.skilltree.SkillTreeNode;
public record ParentInformation(SkillTreeNode node, ParentType type) {
}

View File

@ -6,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,45 +65,29 @@ 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")));
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 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(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)));
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);
}
}
} catch (Exception e) {
MMOCore.log("Couldn't load icons for the skill tree " + id);
e.printStackTrace();
}
}
@ -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();
}
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);
}

View File

@ -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 +
'}';
}
}

View File

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

View File

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

View File

@ -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

View File

@ -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.'
- ''

View File

@ -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

View File

@ -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.'