diff --git a/src/main/java/net/Indyuce/mmocore/api/SoundEvent.java b/src/main/java/net/Indyuce/mmocore/api/SoundEvent.java index d59c2764..efcb287c 100644 --- a/src/main/java/net/Indyuce/mmocore/api/SoundEvent.java +++ b/src/main/java/net/Indyuce/mmocore/api/SoundEvent.java @@ -13,7 +13,8 @@ public enum SoundEvent { SELECT_CLASS, LEVEL_ATTRIBUTE, RESET_ATTRIBUTES, - NOT_ENOUGH_POINTS, + RESET_SKILLS + ,NOT_ENOUGH_POINTS, CANCEL_QUEST, START_QUEST, CLOSE_LOOT_CHEST, diff --git a/src/main/java/net/Indyuce/mmocore/api/player/PlayerData.java b/src/main/java/net/Indyuce/mmocore/api/player/PlayerData.java index 3eec7631..aa1f3fe6 100644 --- a/src/main/java/net/Indyuce/mmocore/api/player/PlayerData.java +++ b/src/main/java/net/Indyuce/mmocore/api/player/PlayerData.java @@ -77,7 +77,7 @@ public class PlayerData extends OfflinePlayerData implements Closable, Experienc */ @Nullable private PlayerClass profess; - private int level, classPoints, skillPoints, attributePoints, attributeReallocationPoints, skillTreeReallocationPoints;// skillReallocationPoints, + private int level, classPoints, skillPoints, attributePoints, attributeReallocationPoints, skillTreeReallocationPoints, skillReallocationPoints; private double experience; private double mana, stamina, stellium; private Guild guild; @@ -416,6 +416,19 @@ public class PlayerData extends OfflinePlayerData implements Closable, Experienc return skillPoints; } + public void giveSkillReallocationPoints(int value) { + skillReallocationPoints+=value; + } + + public int countSkillPointsWhenReallocate() { + int sum = 0; + for(ClassSkill skill:getProfess().getSkills()) { + //0 if the skill is level 1(just unlocked) or 0 locked. + sum+=Math.max(0,getSkillLevel(skill.getSkill())-1); + } + return sum; + } + public int getAttributePoints() { return attributePoints; } @@ -531,6 +544,14 @@ public class PlayerData extends OfflinePlayerData implements Closable, Experienc attributeReallocationPoints = Math.max(0, value); } + public void setSkillReallocationPoints(int value) { + skillReallocationPoints = Math.max(0, value); + } + + public int getSkillReallocationPoints() { + return skillReallocationPoints; + } + public void setSkillPoints(int value) { skillPoints = Math.max(0, value); } @@ -1038,7 +1059,7 @@ public class PlayerData extends OfflinePlayerData implements Closable, Experienc public void setBoundSkill(int slot, ClassSkill skill) { Validate.notNull(skill, "Skill cannot be null"); - if (boundSkills.size() < 6) + if (boundSkills.size() < getProfess().getMaxBoundSkills()) boundSkills.add(skill); else boundSkills.set(slot, skill); diff --git a/src/main/java/net/Indyuce/mmocore/api/player/profess/PlayerClass.java b/src/main/java/net/Indyuce/mmocore/api/player/profess/PlayerClass.java index ba93ec80..54c5f32d 100644 --- a/src/main/java/net/Indyuce/mmocore/api/player/profess/PlayerClass.java +++ b/src/main/java/net/Indyuce/mmocore/api/player/profess/PlayerClass.java @@ -31,12 +31,15 @@ import net.Indyuce.mmocore.loot.chest.particle.CastingParticle; import net.Indyuce.mmocore.player.stats.StatInfo; import net.Indyuce.mmocore.skill.ClassSkill; import net.Indyuce.mmocore.skill.RegisteredSkill; +import net.Indyuce.mmocore.skill.cast.KeyCombo; +import net.Indyuce.mmocore.skill.cast.PlayerKey; import net.md_5.bungee.api.ChatColor; import org.bukkit.Location; import org.bukkit.Material; import org.bukkit.Particle; import org.bukkit.configuration.ConfigurationSection; import org.bukkit.configuration.file.FileConfiguration; +import org.bukkit.craftbukkit.libs.org.apache.commons.lang3.Validate; import org.bukkit.inventory.ItemStack; import org.bukkit.inventory.meta.ItemMeta; import org.jetbrains.annotations.NotNull; @@ -65,10 +68,16 @@ public class PlayerClass extends PostLoadObject implements ExperienceObject { @NotNull private final CastingParticle castParticle; + private final int maxBoundSkills; + private final List classScripts = new LinkedList(); private final Map stats = new HashMap<>(); private final Map skills = new LinkedHashMap<>(); private final List subclasses = new ArrayList<>(); - private final List classScripts = new LinkedList<>(); + + // If the class redefines its own key combos. + private final Map combos = new HashMap<>(); + private int longestCombo; + private final Map resourceHandlers = new HashMap<>(); @Deprecated @@ -110,7 +119,7 @@ public class PlayerClass extends PostLoadObject implements ExperienceObject { ? MMOCore.plugin.experience.getCurveOrThrow( config.get("exp-curve").toString().toLowerCase().replace("_", "-").replace(" ", "-")) : ExpCurve.DEFAULT; - + maxBoundSkills = config.getInt("max-bound-skills", 6); ExperienceTable expTable = null; if (config.contains("exp-table")) try { @@ -132,6 +141,23 @@ public class PlayerClass extends PostLoadObject implements ExperienceObject { MMOCore.plugin.getLogger().log(Level.WARNING, "Could not load script '" + key + "' from class '" + id + "': " + exception.getMessage()); } + // Load different combos + if (config.contains("key-combos")) + for (String key : config.getConfigurationSection("key-combos").getKeys(false)) + try { + int spellSlot = Integer.valueOf(key); + Validate.isTrue(spellSlot >= 0, "Spell slot must be at least 0"); + Validate.isTrue(!combos.values().contains(spellSlot), "There is already a key combo with the same skill slot"); + KeyCombo combo = new KeyCombo(); + for (String str : config.getStringList("key-combos." + key)) + combo.registerKey(PlayerKey.valueOf(UtilityMethods.enumName(str))); + + combos.put(combo, spellSlot); + longestCombo = Math.max(longestCombo, combo.countKeys()); + } catch (RuntimeException exception) { + MMOCore.plugin.getLogger().log(Level.WARNING, "Could not load key combo '" + key + "': " + exception.getMessage()); + } + if (config.contains("triggers")) for (String key : config.getConfigurationSection("triggers").getKeys(false)) try { @@ -227,7 +253,7 @@ public class PlayerClass extends PostLoadObject implements ExperienceObject { this.icon = new ItemStack(material); setOption(ClassOption.DISPLAY, false); setOption(ClassOption.DEFAULT, false); - + maxBoundSkills = 6; for (PlayerResource resource : PlayerResource.values()) resourceHandlers.put(resource, new ResourceRegeneration(resource)); } @@ -283,6 +309,10 @@ public class PlayerClass extends PostLoadObject implements ExperienceObject { return expCurve; } + public int getMaxBoundSkills() { + return maxBoundSkills; + } + @NotNull public ExperienceTable getExperienceTable() { return Objects.requireNonNull(expTable, "Class has no exp table"); @@ -406,6 +436,15 @@ public class PlayerClass extends PostLoadObject implements ExperienceObject { return stats.keySet(); } + @Nullable + public Map getKeyCombos() { + return combos; + } + + public int getLongestCombo() { + return longestCombo; + } + @NotNull private LinearValue getStatInfo(String stat) { LinearValue found = stats.get(stat); diff --git a/src/main/java/net/Indyuce/mmocore/command/CommandVerbose.java b/src/main/java/net/Indyuce/mmocore/command/CommandVerbose.java index ba69e7db..a4a1b08f 100644 --- a/src/main/java/net/Indyuce/mmocore/command/CommandVerbose.java +++ b/src/main/java/net/Indyuce/mmocore/command/CommandVerbose.java @@ -50,6 +50,7 @@ public class CommandVerbose { public enum CommandType { ATTRIBUTE, + SKILL, CLASS, EXPERIENCE, LEVEL, diff --git a/src/main/java/net/Indyuce/mmocore/command/rpg/admin/AdminCommandTreeNode.java b/src/main/java/net/Indyuce/mmocore/command/rpg/admin/AdminCommandTreeNode.java index bda2dafb..721c8f47 100644 --- a/src/main/java/net/Indyuce/mmocore/command/rpg/admin/AdminCommandTreeNode.java +++ b/src/main/java/net/Indyuce/mmocore/command/rpg/admin/AdminCommandTreeNode.java @@ -6,33 +6,35 @@ import net.Indyuce.mmocore.api.player.profess.resource.PlayerResource; import org.bukkit.command.CommandSender; public class AdminCommandTreeNode extends CommandTreeNode { - public AdminCommandTreeNode(CommandTreeNode parent) { - super(parent, "admin"); - - addChild(new HideActionBarCommandTreeNode(this)); - addChild(new NoCooldownCommandTreeNode(this)); - addChild(new ResetCommandTreeNode(this)); - addChild(new InfoCommandTreeNode(this)); - addChild(new ClassCommandTreeNode(this)); - addChild(new ForceClassCommandTreeNode(this)); - addChild(new ExportDataTreeNode(this)); + public AdminCommandTreeNode(CommandTreeNode parent) { + super(parent, "admin"); - addChild(new ExperienceCommandTreeNode(this)); - addChild(new LevelCommandTreeNode(this)); - addChild(new AttributeCommandTreeNode(this)); + addChild(new HideActionBarCommandTreeNode(this)); + addChild(new NoCooldownCommandTreeNode(this)); + addChild(new ResetCommandTreeNode(this)); + addChild(new InfoCommandTreeNode(this)); + addChild(new ClassCommandTreeNode(this)); + addChild(new ForceClassCommandTreeNode(this)); + addChild(new ExportDataTreeNode(this)); - addChild(new PointsCommandTreeNode("skill", this, PlayerData::setSkillPoints, PlayerData::giveSkillPoints, PlayerData::getSkillPoints)); - addChild(new PointsCommandTreeNode("class", this, PlayerData::setClassPoints, PlayerData::giveClassPoints, PlayerData::getClassPoints)); - addChild(new PointsCommandTreeNode("attribute", this, PlayerData::setAttributePoints, PlayerData::giveAttributePoints, PlayerData::getAttributePoints)); - addChild(new PointsCommandTreeNode("attr-realloc", this, PlayerData::setAttributeReallocationPoints, PlayerData::giveAttributeReallocationPoints, PlayerData::getAttributeReallocationPoints)); - addChild(new PointsCommandTreeNode("skill-tree-realloc",this,PlayerData::setSkillTreeReallocationPoints,PlayerData::giveSkillTreeReallocationPoints,PlayerData::getSkillTreeReallocationPoints)); - addChild(new SkillTreePointsCommandTreeNode(this,(playerData,val,id)->playerData.setSkillTreePoints(id,val),(playerData, val, id)->playerData.giveSkillTreePoints(id,val),(playerData,id)->playerData.getSkillTreePoint(id))); - for (PlayerResource res : PlayerResource.values()) - addChild(new ResourceCommandTreeNode(res.name().toLowerCase(), this, res)); - } + addChild(new ExperienceCommandTreeNode(this)); + addChild(new LevelCommandTreeNode(this)); + addChild(new AttributeCommandTreeNode(this)); + addChild(new SkillCommandTreeNode(this)); - @Override - public CommandResult execute(CommandSender sender, String[] args) { - return CommandResult.THROW_USAGE; - } + addChild(new PointsCommandTreeNode("skill", this, PlayerData::setSkillPoints, PlayerData::giveSkillPoints, PlayerData::getSkillPoints)); + addChild(new PointsCommandTreeNode("class", this, PlayerData::setClassPoints, PlayerData::giveClassPoints, PlayerData::getClassPoints)); + addChild(new PointsCommandTreeNode("attribute", this, PlayerData::setAttributePoints, PlayerData::giveAttributePoints, PlayerData::getAttributePoints)); + addChild(new PointsCommandTreeNode("attr-realloc", this, PlayerData::setAttributeReallocationPoints, PlayerData::giveAttributeReallocationPoints, PlayerData::getAttributeReallocationPoints)); + addChild(new PointsCommandTreeNode("skill-realloc", this, PlayerData::setSkillReallocationPoints, PlayerData::giveSkillReallocationPoints, PlayerData::getSkillReallocationPoints)); + addChild(new PointsCommandTreeNode("skill-tree-realloc", this, PlayerData::setSkillTreeReallocationPoints, PlayerData::giveSkillTreeReallocationPoints, PlayerData::getSkillTreeReallocationPoints)); + + for (PlayerResource res : PlayerResource.values()) + addChild(new ResourceCommandTreeNode(res.name().toLowerCase(), this, res)); + } + + @Override + public CommandResult execute(CommandSender sender, String[] args) { + return CommandResult.THROW_USAGE; + } } diff --git a/src/main/java/net/Indyuce/mmocore/command/rpg/admin/SkillCommandTreeNode.java b/src/main/java/net/Indyuce/mmocore/command/rpg/admin/SkillCommandTreeNode.java new file mode 100644 index 00000000..df9f3507 --- /dev/null +++ b/src/main/java/net/Indyuce/mmocore/command/rpg/admin/SkillCommandTreeNode.java @@ -0,0 +1,92 @@ +package net.Indyuce.mmocore.command.rpg.admin; + +import io.lumine.mythic.lib.command.api.CommandTreeNode; +import io.lumine.mythic.lib.command.api.Parameter; +import net.Indyuce.mmocore.MMOCore; +import net.Indyuce.mmocore.api.player.PlayerData; +import net.Indyuce.mmocore.command.CommandVerbose; +import net.Indyuce.mmocore.skill.ClassSkill; +import net.Indyuce.mmocore.skill.RegisteredSkill; +import org.bukkit.Bukkit; +import org.bukkit.ChatColor; +import org.bukkit.command.CommandSender; +import org.bukkit.entity.Player; + +import java.util.Collection; +import java.util.List; +import java.util.function.BiFunction; + + +public class SkillCommandTreeNode extends CommandTreeNode { + public SkillCommandTreeNode(CommandTreeNode parent) { + super(parent, "skill"); + + addChild(new ActionCommandTreeNode(this, "give", (old, amount) -> old + amount)); + addChild(new ActionCommandTreeNode(this, "set", (old, amount) -> amount)); + } + + + public class ActionCommandTreeNode extends CommandTreeNode { + private final BiFunction change; + + public ActionCommandTreeNode(CommandTreeNode parent, String type, BiFunction change) { + super(parent, type); + this.change = change; + addParameter(Parameter.PLAYER); + addParameter(new Parameter("", + (explorer, list) -> MMOCore.plugin.skillManager.getAll().forEach(skill -> list.add(skill.getHandler().getId().toUpperCase())))); + addParameter(Parameter.AMOUNT); + } + + @Override + public CommandResult execute(CommandSender sender, String[] args) { + if (args.length < 6) + return CommandResult.THROW_USAGE; + + Player player = Bukkit.getPlayer(args[3]); + if (player == null) { + sender.sendMessage(ChatColor.RED + "Could not find the player called " + args[3] + "."); + return CommandResult.FAILURE; + } + PlayerData playerData = PlayerData.get(player); + + RegisteredSkill skill = MMOCore.plugin.skillManager.getSkill(args[4]); + if (skill == null) { + sender.sendMessage(ChatColor.RED + "Could not find the skill called " + args[4] + "."); + return CommandResult.FAILURE; + } + + + + ClassSkill classSkill=null; + for(ClassSkill var:playerData.getProfess().getSkills()) { + if(var.getSkill().equals(skill)) + classSkill=var; + } + + if(classSkill==null||classSkill.getUnlockLevel() > playerData.getLevel()) { + sender.sendMessage(ChatColor.RED+ skill.getName()+" is not unlockable for "+player.getName()+"."); + return CommandResult.FAILURE; + } + + + int amount; + try { + amount = Integer.parseInt(args[5]); + } catch (Exception e) { + sender.sendMessage(ChatColor.RED + args[5] + " is not a valid number."); + return CommandResult.FAILURE; + } + int value = change.apply(playerData.getSkillLevel(skill), amount); + playerData.setSkillLevel(skill, value); + CommandVerbose.verbose(sender, CommandVerbose.CommandType.SKILL, ChatColor.GOLD + player.getName() + ChatColor.YELLOW + + " is now level " + ChatColor.GOLD + value + ChatColor.YELLOW + " for " + skill.getName() + "."); + return CommandResult.SUCCESS; + } + } + + @Override + public CommandResult execute(CommandSender sender, String[] args) { + return CommandResult.THROW_USAGE; + } +} diff --git a/src/main/java/net/Indyuce/mmocore/gui/SkillList.java b/src/main/java/net/Indyuce/mmocore/gui/SkillList.java index a16a22e1..2c4fb192 100644 --- a/src/main/java/net/Indyuce/mmocore/gui/SkillList.java +++ b/src/main/java/net/Indyuce/mmocore/gui/SkillList.java @@ -4,6 +4,7 @@ import io.lumine.mythic.lib.MythicLib; import io.lumine.mythic.lib.api.item.ItemTag; import io.lumine.mythic.lib.api.item.NBTItem; import net.Indyuce.mmocore.MMOCore; +import net.Indyuce.mmocore.api.SoundEvent; import net.Indyuce.mmocore.gui.api.InventoryClickContext; import net.Indyuce.mmocore.gui.api.item.InventoryItem; import net.Indyuce.mmocore.gui.api.item.SimplePlaceholderItem; @@ -14,6 +15,7 @@ import net.Indyuce.mmocore.gui.api.GeneratedInventory; import net.Indyuce.mmocore.gui.api.item.Placeholders; import net.Indyuce.mmocore.skill.ClassSkill; import net.Indyuce.mmocore.skill.RegisteredSkill; +import org.bukkit.Bukkit; import org.bukkit.ChatColor; import org.bukkit.Material; import org.bukkit.Sound; @@ -46,6 +48,23 @@ public class SkillList extends EditableInventory { if (function.equals("upgrade")) return new UpgradeItem(config); + if (function.equals("reallocation")) { + + return new InventoryItem(config) { + + @Override + public Placeholders getPlaceholders(GeneratedInventory inv, int n) { + Placeholders holders = new Placeholders(); + holders.register("skill_points", inv.getPlayerData().getSkillPoints()); + holders.register("points", inv.getPlayerData().getSkillReallocationPoints()); + holders.register("total", inv.getPlayerData().countSkillPointsWhenReallocate()); + return holders; + } + }; + } + + + if (function.equals("slot")) return new InventoryItem(config) { private final String none = MythicLib.plugin.parseColors(config.getString("no-skill")); @@ -70,6 +89,9 @@ public class SkillList extends EditableInventory { @Override public ItemStack display(SkillViewerInventory inv, int n) { + if (n >= inv.getPlayerData().getProfess().getMaxBoundSkills()) { + return new ItemStack(Material.AIR); + } ItemStack item = super.display(inv, n); if (!inv.getPlayerData().hasSkillBound(n)) { item.setType(emptyMaterial); @@ -225,27 +247,27 @@ public class SkillList extends EditableInventory { return new Placeholders(); } } - + public class UpgradeItem extends InventoryItem { - private int shiftCost=1; - + private int shiftCost = 1; + public UpgradeItem(ConfigurationSection config) { super(config); - if(config.contains("shift-cost")) { + if (config.contains("shift-cost")) { this.shiftCost = config.getInt("shift-cost"); if (shiftCost < 1) { MMOCore.log(Level.WARNING, "Upgrade shift-cost cannot be less than 1. Using default value: 1"); shiftCost = 1; } } - + } - + @Override public Placeholders getPlaceholders(SkillViewerInventory inv, int n) { RegisteredSkill selected = inv.selected == null ? null : inv.selected.getSkill(); Placeholders holders = new Placeholders(); - + holders.register("skill_caps", selected.getName().toUpperCase()); holders.register("skill", selected.getName()); holders.register("skill_points", "" + inv.getPlayerData().getSkillPoints()); @@ -253,7 +275,7 @@ public class SkillList extends EditableInventory { return holders; } } - + public class SkillViewerInventory extends GeneratedInventory { // Cached information @@ -305,6 +327,35 @@ public class SkillList extends EditableInventory { return; } + if(item.getFunction().equals("reallocation")) { + + + + int spent= getPlayerData().countSkillPointsWhenReallocate(); + + if (spent < 1) { + MMOCore.plugin.configManager.getSimpleMessage("no-skill-points-spent").send(player); + MMOCore.plugin.soundManager.getSound(SoundEvent.NOT_ENOUGH_POINTS).playTo(getPlayer()); + return; + } + + if (playerData.getSkillReallocationPoints() < 1) { + MMOCore.plugin.configManager.getSimpleMessage("not-skill-reallocation-point").send(player); + MMOCore.plugin.soundManager.getSound(SoundEvent.NOT_ENOUGH_POINTS).playTo(getPlayer()); + return; + } + + + for(ClassSkill skill:playerData.getProfess().getSkills()) { + playerData.setSkillLevel(skill.getSkill(), 1); + } + playerData.giveSkillPoints(spent); + playerData.setSkillReallocationPoints(playerData.getSkillReallocationPoints()-1); + MMOCore.plugin.configManager.getSimpleMessage("skill-points-reallocated", "points", "" + playerData.getSkillPoints()).send(player); + MMOCore.plugin.soundManager.getSound(SoundEvent.RESET_SKILLS).playTo(getPlayer()); + open(); + } + if (item.getFunction().equals("previous")) { player.playSound(player.getLocation(), Sound.UI_BUTTON_CLICK, 1, 2); page--; @@ -325,15 +376,13 @@ public class SkillList extends EditableInventory { if (item.getFunction().equals("slot")) { int index = slotSlots.indexOf(context.getSlot()); - - // unbind if there is a current spell. +KEy // unbind if there is a current spell. if (context.getClickType() == ClickType.RIGHT) { if (!playerData.hasSkillBound(index)) { MMOCore.plugin.configManager.getSimpleMessage("no-skill-bound").send(player); player.playSound(player.getLocation(), Sound.ENTITY_VILLAGER_NO, 1, 2); return; } - player.playSound(player.getLocation(), Sound.ENTITY_EXPERIENCE_ORB_PICKUP, 1, 2); playerData.unbindSkill(index); open(); @@ -349,6 +398,7 @@ public class SkillList extends EditableInventory { return; } + if (!playerData.hasSkillUnlocked(selected)) { MMOCore.plugin.configManager.getSimpleMessage("not-unlocked-skill").send(player); player.playSound(player.getLocation(), Sound.ENTITY_VILLAGER_NO, 1, 2); @@ -367,7 +417,7 @@ 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); player.playSound(player.getLocation(), Sound.ENTITY_VILLAGER_NO, 1, 2); @@ -385,21 +435,21 @@ public class SkillList extends EditableInventory { player.playSound(player.getLocation(), Sound.ENTITY_VILLAGER_NO, 1, 2); return; } - + if (context.getClickType().isShiftClick()) { if (playerData.getSkillPoints() < shiftCost) { MMOCore.plugin.configManager.getSimpleMessage("not-enough-skill-points-shift", "shift_points", "" + shiftCost).send(player); player.playSound(player.getLocation(), Sound.ENTITY_VILLAGER_NO, 1, 2); return; } - + playerData.giveSkillPoints(-shiftCost); playerData.setSkillLevel(selected.getSkill(), playerData.getSkillLevel(selected.getSkill()) + shiftCost); } else { playerData.giveSkillPoints(-1); playerData.setSkillLevel(selected.getSkill(), playerData.getSkillLevel(selected.getSkill()) + 1); } - + MMOCore.plugin.configManager.getSimpleMessage("upgrade-skill", "skill", selected.getSkill().getName(), "level", "" + playerData.getSkillLevel(selected.getSkill())).send(player); player.playSound(player.getLocation(), Sound.ENTITY_PLAYER_LEVELUP, 1, 2); diff --git a/src/main/java/net/Indyuce/mmocore/gui/api/EditableInventory.java b/src/main/java/net/Indyuce/mmocore/gui/api/EditableInventory.java index 0d6777a2..90f6239f 100644 --- a/src/main/java/net/Indyuce/mmocore/gui/api/EditableInventory.java +++ b/src/main/java/net/Indyuce/mmocore/gui/api/EditableInventory.java @@ -19,11 +19,11 @@ import java.util.logging.Level; public abstract class EditableInventory { private final String id; + private FileConfiguration config; private AdaptorType adaptorType; private String name; private int slots; - protected double radius, angleGap, verticalGap, curvature, verticalOffset, interactSensitivity; /* * This set is linked so it keeps the order/priority in @@ -39,17 +39,10 @@ public abstract class EditableInventory { } public void reload(FileConfiguration config) { - + this.config = config; this.adaptorType = AdaptorType.valueOf(UtilityMethods.enumName(config.getString("adaptor-type", "classic-adaptor"))); Validate.notNull(adaptorType, config.getString("adaptor-type") + " does not correspond to an adaptor-type.");//TODO - this.radius = config.getDouble("radius", 2); - this.angleGap = config.getDouble("angle-gap", 10); - this.verticalGap = config.getDouble("vertical-gap", 1); - this.curvature = config.getDouble("curvature", 1); - this.verticalOffset = config.getDouble("vertical-offset", 0); - this.interactSensitivity = config.getDouble("interact-sensitivity", 0.97); - this.name = config.getString("name"); Validate.notNull(name, "Name must not be null"); @@ -91,28 +84,9 @@ public abstract class EditableInventory { return adaptorType; } - public double getRadius() { - return radius; - } - public double getAngleGap() { - return angleGap; - } - - public double getVerticalGap() { - return verticalGap; - } - - public double getInteractSensitivity() { - return interactSensitivity; - } - - public double getCurvature() { - return curvature; - } - - public double getVerticalOffset() { - return verticalOffset; + public FileConfiguration getConfig() { + return config; } public InventoryItem getByFunction(String function) { diff --git a/src/main/java/net/Indyuce/mmocore/gui/api/GeneratedInventory.java b/src/main/java/net/Indyuce/mmocore/gui/api/GeneratedInventory.java index 18d77a25..e7c5247f 100644 --- a/src/main/java/net/Indyuce/mmocore/gui/api/GeneratedInventory.java +++ b/src/main/java/net/Indyuce/mmocore/gui/api/GeneratedInventory.java @@ -20,7 +20,6 @@ public abstract class GeneratedInventory extends PluginInventory { public GeneratedInventory(PlayerData playerData, EditableInventory editable) { super(playerData); - this.editable = editable; this.adaptor = editable.getAdaptorType().supply(this); } @@ -30,6 +29,7 @@ public abstract class GeneratedInventory extends PluginInventory { } public EditableInventory getEditable() { + return editable; } @@ -62,8 +62,8 @@ public abstract class GeneratedInventory extends PluginInventory { return ((ClassicAdaptor) adaptor).getInventory(); } + @Override public void open() { - /* * Very important, in order to prevent ghost items, the loaded items map * must be cleared when the inventory is updated or open at least twice @@ -75,7 +75,7 @@ public abstract class GeneratedInventory extends PluginInventory { /** * @deprecated Not a fan of that implementation. - * Better work with {@link InventoryItem#setDisplayed(Inventory, GeneratedInventory)} + * Better work with {@link InventoryItem#setDisplayed(Inventory, GeneratedInventory)} */ @Deprecated public void dynamicallyUpdateItem(InventoryItem item, int n, ItemStack placed, Consumer update) { @@ -83,6 +83,7 @@ public abstract class GeneratedInventory extends PluginInventory { } + @Override public void whenClicked(InventoryClickContext context) { context.setCancelled(true); InventoryItem item = getBySlot(context.getSlot()); diff --git a/src/main/java/net/Indyuce/mmocore/gui/api/adaptor/ThreeDimAdaptor.java b/src/main/java/net/Indyuce/mmocore/gui/api/adaptor/ThreeDimAdaptor.java index 579e959e..605ba6b9 100644 --- a/src/main/java/net/Indyuce/mmocore/gui/api/adaptor/ThreeDimAdaptor.java +++ b/src/main/java/net/Indyuce/mmocore/gui/api/adaptor/ThreeDimAdaptor.java @@ -48,10 +48,13 @@ public class ThreeDimAdaptor extends Adaptor { private final HashMap hiddenArmorStand = new HashMap<>(); + protected double radius, angleGap, verticalGap, curvature, verticalOffset, interactSensitivity; + + private boolean firstTime = true; private final Vector direction = generated.getPlayer().getEyeLocation().getDirection().setY(0); - private final Location location = generated.getPlayer().getLocation().add(new Vector(0, generated.getEditable().getVerticalOffset(), 0)); + private final Location location; //Zoomed=-1 no armorstand are under zoom private int zoomed = -1; @@ -59,6 +62,14 @@ public class ThreeDimAdaptor extends Adaptor { public ThreeDimAdaptor(GeneratedInventory generated) { super(generated); + + this.radius = generated.getEditable().getConfig().getDouble("radius", 2); + this.angleGap = generated.getEditable().getConfig().getDouble("angle-gap", 10); + this.verticalGap = generated.getEditable().getConfig().getDouble("vertical-gap", 1); + this.curvature = generated.getEditable().getConfig().getDouble("curvature", 1); + this.verticalOffset = generated.getEditable().getConfig().getDouble("vertical-offset", 0); + this.interactSensitivity = generated.getEditable().getConfig().getDouble("interact-sensitivity", 0.97); + location = generated.getPlayer().getLocation().add(new Vector(0, verticalOffset, 0)); } @@ -180,9 +191,12 @@ public class ThreeDimAdaptor extends Adaptor { private void setItem(ItemStack item, int n, double percentage) { Location location = getLocation(n, percentage); + if(item.getType().toString().contains("SKULL")||item.getType().toString().contains("HEAD")) { + location.add(new Vector(0,4.9,0)); + } //We create the armorStand corresponding to display the item ArmorStand armorStand = (ArmorStand) generated.getPlayer().getWorld().spawnEntity(location, EntityType.ARMOR_STAND); - armorStand.setVisible(false); + armorStand.setVisible(true); armorStand.setSmall(false); armorStand.setArms(true); armorStand.setGravity(false); @@ -317,13 +331,13 @@ public class ThreeDimAdaptor extends Adaptor { Location cloneLocation = location.clone(); Vector cloneDirection = direction.clone().rotateAroundAxis(new Vector(0, 1, 0), - -((n % 9) - 4) * generated.getEditable().getAngleGap() * Math.PI / 180); + -((n % 9) - 4) * angleGap * Math.PI / 180); //Curvature of 1: r=cst Curvature of 1: r=R/cos(angle) (a plane) - double radius = percentage * generated.getEditable().getRadius() / Math.cos((1 - generated.getEditable().getCurvature()) - * -((n % 9) - 4) * generated.getEditable().getAngleGap() * Math.PI / 180); + double radius = percentage * this.radius / Math.cos((1 - curvature) + * -((n % 9) - 4) * angleGap * Math.PI / 180); cloneDirection = cloneDirection.normalize().multiply(radius); - cloneDirection.add(new Vector(0, percentage * generated.getEditable().getVerticalGap() * ((generated.getEditable().getSlots() - n - 1) / 9), 1)); + cloneDirection.add(new Vector(0, percentage * verticalGap * ((generated.getEditable().getSlots() - n - 1) / 9), 1)); //We get the final direction cloneLocation.add(cloneDirection); @@ -380,33 +394,36 @@ public class ThreeDimAdaptor extends Adaptor { if (!e.getFrom().getBlock().getLocation().equals(e.getTo().getBlock().getLocation())) ThreeDimAdaptor.this.close(); else { - //If the player no longer looks at the zoom as: - if (zoomed != -1 && generated.getPlayer().getLocation().getDirection().normalize().dot( - armorStands.get(zoomed).getLocation().add(new Vector(0, 0.25 * armorStands.get(zoomed).getHeight(), 0)) - .subtract(generated.getPlayer().getLocation()).toVector().normalize()) < generated.getEditable().getInteractSensitivity()) { - armorStands.get(zoomed).teleport(getLocation(zoomed, 1)); - zoomed = -1; - removeLore(); + int closest = -1; + double closestScalar = 1; + for (int n : armorStands.keySet()) { + ArmorStand as = armorStands.get(n); + Location asLocation = as.getLocation().add(new Vector(0, 0.25 * as.getHeight(), 0)); + + + double scalar = generated.getPlayer().getLocation().getDirection().normalize().dot( + asLocation.subtract(generated.getPlayer().getLocation()).toVector().normalize()); + + if (scalar > interactSensitivity && scalar < closestScalar) { + closestScalar = scalar; + closest = n; + } } - if (zoomed == -1) { - for (int n : armorStands.keySet()) { - ArmorStand as = armorStands.get(n); - Location asLocation = as.getLocation().add(new Vector(0, 0.25 * as.getHeight(), 0)); - - - double scalar = generated.getPlayer().getLocation().getDirection().normalize().dot( - asLocation.subtract(generated.getPlayer().getLocation()).toVector().normalize()); - - if (scalar > generated.getEditable().getInteractSensitivity()) { - as.teleport(getLocation(n, 0.75)); - zoomed = n; - displayLore(zoomed); - } - + if (closest != zoomed) { + if (zoomed != -1) { + removeLore(); + armorStands.get(zoomed).teleport(getLocation(zoomed, 1)); + } + if (closest != -1) { + armorStands.get(zoomed).teleport(getLocation(closest, 0.75)); + zoomed = closest; + displayLore(closest); } } + + } } @@ -416,7 +433,11 @@ public class ThreeDimAdaptor extends Adaptor { @EventHandler public void onInteract(PlayerInteractAtEntityEvent event) { if (event.getPlayer().equals(generated.getPlayer())) - if (event.getRightClicked() instanceof ArmorStand armorStand) { + if (event.getRightClicked() instanceof ArmorStand armorStand) + event.setCancelled(true); + + + /* if (armorStands.values().contains(armorStand)) { PersistentDataContainer container = armorStand.getPersistentDataContainer(); int slot = container.get(new NamespacedKey(MMOCore.plugin, "slot"), PersistentDataType.INTEGER); @@ -428,6 +449,7 @@ public class ThreeDimAdaptor extends Adaptor { generated.whenClicked(new InventoryClickContext(slot, armorStand.getEquipment().getItem(EquipmentSlot.HEAD), clickType, event)); } } + */ } @EventHandler @@ -465,35 +487,40 @@ public class ThreeDimAdaptor extends Adaptor { } } - @EventHandler public void onDamage(EntityDamageByEntityEvent event) { - - if (event.getDamager() instanceof Player player) { + if (event.getDamager() instanceof Player player) if (player.equals(generated.getPlayer())) - if (event.getEntity() instanceof ArmorStand armorStand) { - if (armorStands.values().contains(armorStand)) { - PersistentDataContainer container = armorStand.getPersistentDataContainer(); - int slot = container.get(new NamespacedKey(MMOCore.plugin, "slot"), PersistentDataType.INTEGER); - ClickType clickType; - if (player.isSneaking()) - clickType = ClickType.SHIFT_LEFT; - else - clickType = ClickType.LEFT; - - ItemStack itemStack = armorStand.getEquipment().getItem(EquipmentSlot.HEAD); - generated.whenClicked(new InventoryClickContext(slot, itemStack, clickType, event)); - } - } - } - + if (event.getEntity() instanceof ArmorStand armorStand) + if (armorStands.values().contains(armorStand)) + event.setCancelled(true); } + + /* + PersistentDataContainer container = armorStand.getPersistentDataContainer(); + int slot = container.get(new NamespacedKey(MMOCore.plugin, "slot"), PersistentDataType.INTEGER); + ClickType clickType; + if(player.isSneaking()) + clickType =ClickType.SHIFT_LEFT; + else + clickType =ClickType.LEFT; + + ItemStack itemStack = armorStand.getEquipment().getItem(EquipmentSlot.HEAD); + generated.whenClicked(new + + InventoryClickContext(slot, itemStack, clickType, event)); +} + } + } + + */ + + @Override public void whenClosed() { } - } } diff --git a/src/main/java/net/Indyuce/mmocore/listener/event/PlayerPressKeyListener.java b/src/main/java/net/Indyuce/mmocore/listener/event/PlayerPressKeyListener.java index 92afba3b..ef23bbe4 100644 --- a/src/main/java/net/Indyuce/mmocore/listener/event/PlayerPressKeyListener.java +++ b/src/main/java/net/Indyuce/mmocore/listener/event/PlayerPressKeyListener.java @@ -13,6 +13,7 @@ import org.bukkit.event.player.PlayerDropItemEvent; import org.bukkit.event.player.PlayerInteractEvent; import org.bukkit.event.player.PlayerSwapHandItemsEvent; import org.bukkit.event.player.PlayerToggleSneakEvent; +import org.bukkit.inventory.EquipmentSlot; /** * This registers all the KeyPress events. All events are registered @@ -32,7 +33,7 @@ public class PlayerPressKeyListener implements Listener { @EventHandler(priority = EventPriority.LOWEST) public void registerClickKey(PlayerInteractEvent event) { - if (event.useItemInHand() != Event.Result.DENY && event.getAction().name().contains("CLICK")) { + if (event.useItemInHand() != Event.Result.DENY && event.getAction().name().contains("CLICK")&&event.getHand().equals(EquipmentSlot.HAND)) { boolean rightClick = event.getAction() == Action.RIGHT_CLICK_AIR || event.getAction() == Action.RIGHT_CLICK_BLOCK; PlayerKeyPressEvent called = new PlayerKeyPressEvent(PlayerData.get(event.getPlayer()), rightClick ? PlayerKey.RIGHT_CLICK : PlayerKey.LEFT_CLICK, event); Bukkit.getPluginManager().callEvent(called); diff --git a/src/main/java/net/Indyuce/mmocore/manager/ConfigManager.java b/src/main/java/net/Indyuce/mmocore/manager/ConfigManager.java index fe16fce1..3f5d2784 100644 --- a/src/main/java/net/Indyuce/mmocore/manager/ConfigManager.java +++ b/src/main/java/net/Indyuce/mmocore/manager/ConfigManager.java @@ -28,7 +28,7 @@ public class ConfigManager { public ChatColor staminaFull, staminaHalf, staminaEmpty; public long combatLogTimer, lootChestExpireTime, lootChestPlayerCooldown, globalSkillCooldown; public double lootChestsChanceWeight, fishingDropsChanceWeight; - public int maxPartyLevelDifference; + public int maxPartyLevelDifference,maxBoundSkills; private final FileConfiguration messages; @@ -116,6 +116,7 @@ public class ConfigManager { canCreativeCast = MMOCore.plugin.getConfig().getBoolean("can-creative-cast"); cobbleGeneratorXP = MMOCore.plugin.getConfig().getBoolean("should-cobblestone-generators-give-exp"); saveDefaultClassInfo = MMOCore.plugin.getConfig().getBoolean("save-default-class-info"); + maxBoundSkills= MMOCore.plugin.getConfig().getInt("max-bound-skills",6); } private ChatColor getColorOrDefault(String key, ChatColor defaultColor) { diff --git a/src/main/java/net/Indyuce/mmocore/manager/SkillManager.java b/src/main/java/net/Indyuce/mmocore/manager/SkillManager.java index 2f95fdb1..034a374b 100644 --- a/src/main/java/net/Indyuce/mmocore/manager/SkillManager.java +++ b/src/main/java/net/Indyuce/mmocore/manager/SkillManager.java @@ -26,6 +26,7 @@ public class SkillManager implements MMOCoreManager { skills.put(skill.getHandler().getId().toUpperCase(), skill); } + @Nullable public RegisteredSkill getSkill(String id) { return skills.get(id.toUpperCase()); diff --git a/src/main/java/net/Indyuce/mmocore/manager/data/PlayerDataManager.java b/src/main/java/net/Indyuce/mmocore/manager/data/PlayerDataManager.java index dbd35a15..8e7ce4d9 100644 --- a/src/main/java/net/Indyuce/mmocore/manager/data/PlayerDataManager.java +++ b/src/main/java/net/Indyuce/mmocore/manager/data/PlayerDataManager.java @@ -16,7 +16,7 @@ import java.util.*; public abstract class PlayerDataManager { private final static Map data = Collections.synchronizedMap(new HashMap<>()); - private DefaultPlayerData defaultData = new DefaultPlayerData(1, 0, 0, 0, 0); + private DefaultPlayerData defaultData = new DefaultPlayerData(1, 0, 0, 0, 0,0); public PlayerData get(OfflinePlayer player) { return get(player.getUniqueId()); @@ -134,7 +134,7 @@ public abstract class PlayerDataManager { public abstract void saveData(PlayerData data); public class DefaultPlayerData { - private final int level, classPoints, skillPoints, attributePoints, attrReallocPoints; + private final int level, classPoints, skillPoints, attributePoints, attrReallocPoints,skillReallocPoints; public DefaultPlayerData(ConfigurationSection config) { level = config.getInt("level", 1); @@ -142,14 +142,17 @@ public abstract class PlayerDataManager { skillPoints = config.getInt("skill-points"); attributePoints = config.getInt("attribute-points"); attrReallocPoints = config.getInt("attribute-realloc-points"); + skillReallocPoints=config.getInt("skill-realloc-points",0); } - public DefaultPlayerData(int level, int classPoints, int skillPoints, int attributePoints, int attrReallocPoints) { + public DefaultPlayerData(int level, int classPoints, int skillPoints, int attributePoints, int attrReallocPoints,int skillReallocPoints) { this.level = level; this.classPoints = classPoints; this.skillPoints = skillPoints; this.attributePoints = attributePoints; this.attrReallocPoints = attrReallocPoints; + this.skillReallocPoints=skillReallocPoints; + } public int getLevel() { @@ -172,6 +175,10 @@ public abstract class PlayerDataManager { return attributePoints; } + public int getSkillReallocPoints() { + return skillReallocPoints; + } + public void apply(PlayerData player) { player.setLevel(level); player.setClassPoints(classPoints); diff --git a/src/main/java/net/Indyuce/mmocore/manager/data/mysql/MySQLPlayerDataManager.java b/src/main/java/net/Indyuce/mmocore/manager/data/mysql/MySQLPlayerDataManager.java index a835c594..bed11ea0 100644 --- a/src/main/java/net/Indyuce/mmocore/manager/data/mysql/MySQLPlayerDataManager.java +++ b/src/main/java/net/Indyuce/mmocore/manager/data/mysql/MySQLPlayerDataManager.java @@ -35,6 +35,11 @@ public class MySQLPlayerDataManager extends PlayerDataManager { BukkitRunnable runnable = new BukkitRunnable() { @Override public void run() { + //To prevent infinite loops + if (System.currentTimeMillis() - startTime > 4000) { + cancel(); + return; + } provider.getResult("SELECT * FROM mmocore_playerdata WHERE uuid = '" + data.getUniqueId() + "';", (result) -> { try { @@ -55,6 +60,7 @@ public class MySQLPlayerDataManager extends PlayerDataManager { data.setClassPoints(result.getInt("class_points")); data.setSkillPoints(result.getInt("skill_points")); + data.setSkillReallocationPoints(result.getInt("skill_reallocation_points")); data.setAttributePoints(result.getInt("attribute_points")); data.setAttributeReallocationPoints(result.getInt("attribute_realloc_points")); data.setLevel(result.getInt("level")); @@ -121,6 +127,7 @@ public class MySQLPlayerDataManager extends PlayerDataManager { data.setLevel(getDefaultData().getLevel()); data.setClassPoints(getDefaultData().getClassPoints()); data.setSkillPoints(getDefaultData().getSkillPoints()); + data.setSkillReallocationPoints(getDefaultData().getSkillReallocPoints()); data.setAttributePoints(getDefaultData().getAttributePoints()); data.setAttributeReallocationPoints(getDefaultData().getAttrReallocPoints()); data.setExperience(0); @@ -145,11 +152,12 @@ public class MySQLPlayerDataManager extends PlayerDataManager { @Override public void saveData(PlayerData data) { - MySQLTableEditor sql = new MySQLTableEditor(MySQLTableEditor.Table.PLAYERDATA, data.getUniqueId(),provider); + MySQLTableEditor sql = new MySQLTableEditor(MySQLTableEditor.Table.PLAYERDATA, data.getUniqueId(), provider); MMOCore.sqlDebug("Saving data for: '" + data.getUniqueId() + "'..."); sql.updateData("class_points", data.getClassPoints()); sql.updateData("skill_points", data.getSkillPoints()); + sql.updateData("skill_reallocation_points",data.getSkillReallocationPoints()); sql.updateData("attribute_points", data.getAttributePoints()); sql.updateData("attribute_realloc_points", data.getAttributeReallocationPoints()); sql.updateData("level", data.getLevel()); diff --git a/src/main/java/net/Indyuce/mmocore/manager/data/yaml/YAMLPlayerDataManager.java b/src/main/java/net/Indyuce/mmocore/manager/data/yaml/YAMLPlayerDataManager.java index 6e6ddddb..c5872389 100644 --- a/src/main/java/net/Indyuce/mmocore/manager/data/yaml/YAMLPlayerDataManager.java +++ b/src/main/java/net/Indyuce/mmocore/manager/data/yaml/YAMLPlayerDataManager.java @@ -30,6 +30,7 @@ public class YAMLPlayerDataManager extends PlayerDataManager { data.setClassPoints(config.getInt("class-points", getDefaultData().getClassPoints())); data.setSkillPoints(config.getInt("skill-points", getDefaultData().getSkillPoints())); + data.setSkillReallocationPoints(config.getInt("skill-reallocation-points",getDefaultData().getSkillReallocPoints())); data.setAttributePoints(config.getInt("attribute-points", getDefaultData().getAttributePoints())); data.setAttributeReallocationPoints(config.getInt("attribute-realloc-points", getDefaultData().getAttrReallocPoints())); data.setLevel(config.getInt("level", getDefaultData().getLevel())); @@ -90,6 +91,7 @@ public class YAMLPlayerDataManager extends PlayerDataManager { config.set("class-points", data.getClassPoints()); config.set("skill-points", data.getSkillPoints()); + config.set("skill-reallocation-points",data.getSkillReallocationPoints()); config.set("attribute-points", data.getAttributePoints()); // config.set("skill-realloc-points", skillReallocationPoints); config.set("attribute-realloc-points", data.getAttributeReallocationPoints()); diff --git a/src/main/java/net/Indyuce/mmocore/skill/cast/listener/KeyCombos.java b/src/main/java/net/Indyuce/mmocore/skill/cast/listener/KeyCombos.java index f59d888d..4f2bfa5e 100644 --- a/src/main/java/net/Indyuce/mmocore/skill/cast/listener/KeyCombos.java +++ b/src/main/java/net/Indyuce/mmocore/skill/cast/listener/KeyCombos.java @@ -49,13 +49,6 @@ public class KeyCombos implements Listener { @Nullable private final SoundObject beginComboSound, comboClickSound, failComboSound; - /** - * Essentially the inverse of the {@link #combos} map. This maps - * the skill slot to the corresponding key combo. There's no problem - * because the maps are 100% bijective - */ - private static final Map PUBLIC_COMBOS = new HashMap<>(); - public KeyCombos(ConfigurationSection config) { int longestCombo = 0; @@ -72,8 +65,6 @@ public class KeyCombos implements Listener { combos.put(combo, spellSlot); longestCombo = Math.max(longestCombo, combo.countKeys()); - - PUBLIC_COMBOS.put(spellSlot, combo); } catch (RuntimeException exception) { MMOCore.plugin.getLogger().log(Level.WARNING, "Could not load key combo '" + key + "': " + exception.getMessage()); } @@ -112,6 +103,7 @@ public class KeyCombos implements Listener { } + // Adding pressed key CustomSkillCastingHandler casting = (CustomSkillCastingHandler) playerData.getSkillCasting(); casting.current.registerKey(event.getPressed()); @@ -123,8 +115,8 @@ public class KeyCombos implements Listener { event.setCancelled(true); // Hash current combo and check - if (combos.containsKey(casting.current)) { - int spellSlot = combos.get(casting.current) - 1; + if (casting.classCombos.containsKey(casting.current)) { + int spellSlot = casting.classCombos.get(casting.current) - 1; playerData.leaveCastingMode(); // Cast spell @@ -136,7 +128,7 @@ public class KeyCombos implements Listener { } // Check if current combo is too large - if (casting.current.countKeys() >= longestCombo) { + if (casting.current.countKeys() >= casting.classLongestCombo) { playerData.leaveCastingMode(); if (failComboSound != null) failComboSound.playTo(player); @@ -167,20 +159,36 @@ public class KeyCombos implements Listener { event.setCancelled(true); } + + /** + * Loads the player current combos & the combos applicable to the player (combos defined in its class or the default combos of the config.yml) + */ private class CustomSkillCastingHandler extends SkillCastingHandler { private final KeyCombo current = new KeyCombo(); + //Combos used: default combos from the config or the combos defined in the player class. + private final Map classCombos; + private int classLongestCombo; CustomSkillCastingHandler(PlayerData caster) { super(caster, 10); + if (!caster.getProfess().getKeyCombos().isEmpty()) { + classCombos=caster.getProfess().getKeyCombos(); + classLongestCombo=caster.getProfess().getLongestCombo(); + } else { + classCombos = combos; + classLongestCombo=longestCombo; + } } @Override public void onTick() { if (actionBarOptions != null) - getCaster().displayActionBar(actionBarOptions.format(current)); + getCaster().displayActionBar(actionBarOptions.format(this)); } + } + private class ActionBarOptions { private final String separator, noKey; @@ -198,18 +206,19 @@ public class KeyCombos implements Listener { keyNames.put(key, Objects.requireNonNull(config.getString("key-name." + key.name()), "Could not find translation for key " + key.name())); } - public String format(KeyCombo currentCombo) { + public String format(CustomSkillCastingHandler casting) { // Join all keys with separator - String builder = currentCombo.countKeys() == 0 ? noKey : keyNames.get(currentCombo.getAt(0)); + String builder = casting.current.countKeys() == 0 ? noKey : keyNames.get(casting.current.getAt(0)); int j = 1; - for (; j < currentCombo.countKeys(); j++) - builder += separator + keyNames.get(currentCombo.getAt(j)); + for (; j < casting.current.countKeys(); j++) + builder += separator + keyNames.get(casting.current.getAt(j)); // All remaining - for (; j < longestCombo; j++) + for (; j < casting.classLongestCombo; j++) builder += separator + noKey; + return MythicLib.plugin.parseColors(builder); } } diff --git a/src/main/java/net/Indyuce/mmocore/skill/cast/listener/SkillBar.java b/src/main/java/net/Indyuce/mmocore/skill/cast/listener/SkillBar.java index 99329793..04197b73 100644 --- a/src/main/java/net/Indyuce/mmocore/skill/cast/listener/SkillBar.java +++ b/src/main/java/net/Indyuce/mmocore/skill/cast/listener/SkillBar.java @@ -17,124 +17,131 @@ import org.bukkit.entity.Player; import org.bukkit.event.EventHandler; import org.bukkit.event.Listener; import org.bukkit.event.player.PlayerItemHeldEvent; +import org.bukkit.scheduler.BukkitRunnable; import java.util.Objects; public class SkillBar implements Listener { - private final PlayerKey mainKey; + private final PlayerKey mainKey; - public SkillBar(ConfigurationSection config) { - mainKey = PlayerKey.valueOf(UtilityMethods.enumName(Objects.requireNonNull(config.getString("open"), "Could not find open key"))); - } + public SkillBar(ConfigurationSection config) { + mainKey = PlayerKey.valueOf(UtilityMethods.enumName(Objects.requireNonNull(config.getString("open"), "Could not find open key"))); + } - @EventHandler - public void a(PlayerKeyPressEvent event) { - if (event.getPressed() != mainKey) return; + @EventHandler + public void a(PlayerKeyPressEvent event) { + if (event.getPressed() != mainKey) return; - // Always cancel event - if (event.getPressed().shouldCancelEvent()) event.setCancelled(true); + // Always cancel event + if (event.getPressed().shouldCancelEvent()) event.setCancelled(true); - // Enter spell casting - Player player = event.getData().getPlayer(); - PlayerData playerData = event.getData(); - if (player.getGameMode() != GameMode.SPECTATOR && (MMOCore.plugin.configManager.canCreativeCast || player.getGameMode() != GameMode.CREATIVE) && !playerData.isCasting() && !playerData.getBoundSkills() - .isEmpty()) { - playerData.setSkillCasting(new CustomSkillCastingHandler(playerData)); - MMOCore.plugin.soundManager.getSound(SoundEvent.SPELL_CAST_BEGIN).playTo(player); - } - } + // Enter spell casting + Player player = event.getData().getPlayer(); + PlayerData playerData = event.getData(); + if (player.getGameMode() != GameMode.SPECTATOR && (MMOCore.plugin.configManager.canCreativeCast || player.getGameMode() != GameMode.CREATIVE) && !playerData.isCasting() && !playerData.getBoundSkills() + .isEmpty()) { + playerData.setSkillCasting(new CustomSkillCastingHandler(playerData)); + MMOCore.plugin.soundManager.getSound(SoundEvent.SPELL_CAST_BEGIN).playTo(player); + } + } - private class CustomSkillCastingHandler extends SkillCastingHandler { - private final String ready = MMOCore.plugin.configManager.getSimpleMessage("casting.action-bar.ready").message(); - private final String onCooldown = MMOCore.plugin.configManager.getSimpleMessage("casting.action-bar.on-cooldown").message(); - private final String noMana = MMOCore.plugin.configManager.getSimpleMessage("casting.action-bar.no-mana").message(); - private final String noStamina = MMOCore.plugin.configManager.getSimpleMessage("casting.action-bar.no-stamina").message(); - private final String split = MMOCore.plugin.configManager.getSimpleMessage("casting.split").message(); + private class CustomSkillCastingHandler extends SkillCastingHandler { + private final String ready = MMOCore.plugin.configManager.getSimpleMessage("casting.action-bar.ready").message(); + private final String onCooldown = MMOCore.plugin.configManager.getSimpleMessage("casting.action-bar.on-cooldown").message(); + private final String noMana = MMOCore.plugin.configManager.getSimpleMessage("casting.action-bar.no-mana").message(); + private final String noStamina = MMOCore.plugin.configManager.getSimpleMessage("casting.action-bar.no-stamina").message(); + private final String split = MMOCore.plugin.configManager.getSimpleMessage("casting.split").message(); - private int j; + private int j; - CustomSkillCastingHandler(PlayerData playerData) { - super(playerData, 1); - } + CustomSkillCastingHandler(PlayerData playerData) { + super(playerData, 1); + } - @EventHandler - public void onSkillCast(PlayerItemHeldEvent event) { - Player player = event.getPlayer(); - if (!getCaster().isOnline()) return; - if (!event.getPlayer().equals(getCaster().getPlayer())) return; + @EventHandler + public void onSkillCast(PlayerItemHeldEvent event) { + Player player = event.getPlayer(); + if (!getCaster().isOnline()) return; + if (!event.getPlayer().equals(getCaster().getPlayer())) return; - /* - * When the event is cancelled, another playerItemHeldEvent is - * called and previous and next slots are equal. the event must not - * listen to that non-player called event. - */ - if (event.getPreviousSlot() == event.getNewSlot()) return; + /* + * When the event is cancelled, another playerItemHeldEvent is + * called and previous and next slots are equal. the event must not + * listen to that non-player called event. + */ + if (event.getPreviousSlot() == event.getNewSlot()) return; - event.setCancelled(true); - int slot = event.getNewSlot() + (event.getNewSlot() >= player.getInventory().getHeldItemSlot() ? -1 : 0); + event.setCancelled(true); + int slot = event.getNewSlot() + (event.getNewSlot() >= player.getInventory().getHeldItemSlot() ? -1 : 0); - /* - * The event is called again soon after the first since when - * cancelling the first one, the player held item slot must go back - * to the previous one. - */ - if (slot >= 0 && getCaster().hasSkillBound(slot)) { - PlayerMetadata caster = getCaster().getMMOPlayerData().getStatMap().cache(EquipmentSlot.MAIN_HAND); - getCaster().getBoundSkill(slot).toCastable(getCaster()).cast(new TriggerMetadata(caster, null, null)); - } - } + /* + * The event is called again soon after the first since when + * cancelling the first one, the player held item slot must go back + * to the previous one. + */ + if (slot >= 0 && getCaster().hasSkillBound(slot)) { + PlayerMetadata caster = getCaster().getMMOPlayerData().getStatMap().cache(EquipmentSlot.MAIN_HAND); + getCaster().getBoundSkill(slot).toCastable(getCaster()).cast(new TriggerMetadata(caster, null, null)); + } + } - @EventHandler - public void stopCasting(PlayerKeyPressEvent event) { - Player player = event.getPlayer(); - if (event.getPressed() == mainKey && event.getPlayer().equals(getCaster().getPlayer())) { - MMOCore.plugin.soundManager.getSound(SoundEvent.SPELL_CAST_END).playTo(player); - MMOCore.plugin.configManager.getSimpleMessage("casting.no-longer").send(getCaster().getPlayer()); - PlayerData.get(player).leaveCastingMode(); - } - } + @EventHandler + public void stopCasting(PlayerKeyPressEvent event) { + Player player = event.getPlayer(); + if (event.getPressed() == mainKey && event.getPlayer().equals(getCaster().getPlayer())) { + MMOCore.plugin.soundManager.getSound(SoundEvent.SPELL_CAST_END).playTo(player); - private String getFormat(PlayerData data) { - StringBuilder str = new StringBuilder(); - if (!data.isOnline()) return str.toString(); - for (int j = 0; j < data.getBoundSkills().size(); j++) { - ClassSkill skill = data.getBoundSkill(j); - str.append((str.length() == 0) ? "" : 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}", - "" + (j + 1 + (data.getPlayer().getInventory().getHeldItemSlot() <= j ? 1 : 0))) - .replace("{skill}", data.getBoundSkill(j).getSkill().getName())); - } - return MMOCore.plugin.placeholderParser.parse(data.getPlayer(),str.toString()); - } + new BukkitRunnable() { + @Override + public void run() { + MMOCore.plugin.configManager.getSimpleMessage("casting.no-longer").send(getCaster().getPlayer()); + } + }.runTask(MMOCore.plugin); + PlayerData.get(player).leaveCastingMode(); + } + } - /** - * We don't even need to check if the skill has the 'cooldown' - * modifier. We just look for an entry in the cooldown map which - * won't be here if the skill has no cooldown. - */ - private boolean onCooldown(PlayerData data, ClassSkill skill) { - return data.getCooldownMap().isOnCooldown(skill); - } + private String getFormat(PlayerData data) { + StringBuilder str = new StringBuilder(); + if (!data.isOnline()) return str.toString(); + for (int j = 0; j < data.getBoundSkills().size(); j++) { + ClassSkill skill = data.getBoundSkill(j); + str.append((str.length() == 0) ? "" : 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}", + "" + (j + 1 + (data.getPlayer().getInventory().getHeldItemSlot() <= j ? 1 : 0))) + .replace("{skill}", data.getBoundSkill(j).getSkill().getName())); + } + return MMOCore.plugin.placeholderParser.parse(data.getPlayer(), str.toString()); + } - private boolean noMana(PlayerData data, ClassSkill skill) { - return skill.getSkill().hasModifier("mana") && skill.getModifier("mana", data.getSkillLevel(skill.getSkill())) > data.getMana(); - } + /** + * We don't even need to check if the skill has the 'cooldown' + * modifier. We just look for an entry in the cooldown map which + * won't be here if the skill has no cooldown. + */ + private boolean onCooldown(PlayerData data, ClassSkill skill) { + return data.getCooldownMap().isOnCooldown(skill); + } - private boolean noStamina(PlayerData data, ClassSkill skill) { - return skill.getSkill().hasModifier("stamina") && skill.getModifier("stamina", - data.getSkillLevel(skill.getSkill())) > data.getStamina(); - } + private boolean noMana(PlayerData data, ClassSkill skill) { + return skill.getSkill().hasModifier("mana") && skill.getModifier("mana", data.getSkillLevel(skill.getSkill())) > data.getMana(); + } - @Override - public void onTick() { - if (j % 20 == 0) getCaster().displayActionBar(getFormat(getCaster())); + private boolean noStamina(PlayerData data, ClassSkill skill) { + return skill.getSkill().hasModifier("stamina") && skill.getModifier("stamina", + data.getSkillLevel(skill.getSkill())) > data.getStamina(); + } - for (int k = 0; k < 2; k++) { - double a = (double) j++ / 5; - getCaster().getProfess().getCastParticle() - .display(getCaster().getPlayer().getLocation().add(Math.cos(a), 1 + Math.sin(a / 3) / 1.3, Math.sin(a))); - } - } - } + @Override + public void onTick() { + if (j % 20 == 0) getCaster().displayActionBar(getFormat(getCaster())); + + for (int k = 0; k < 2; k++) { + double a = (double) j++ / 5; + getCaster().getProfess().getCastParticle() + .display(getCaster().getPlayer().getLocation().add(Math.cos(a), 1 + Math.sin(a / 3) / 1.3, Math.sin(a))); + } + } + } } diff --git a/src/main/resources/config.yml b/src/main/resources/config.yml index 7ba96ca0..a164f467 100644 --- a/src/main/resources/config.yml +++ b/src/main/resources/config.yml @@ -31,9 +31,11 @@ default-playerdata: level: 1 class-points: 0 skill-points: 0 + skill-realloc-points: 0 attribute-points: 0 attribute-realloc-points: 0 + # The list of all conditions which must be met for the # BLOCK REGEN and BLOCK RESTRICTIONS to apply. Set to # 'custom-mine-conditions: []' to disable custom mining entirely. @@ -89,6 +91,7 @@ skill-casting: mode: SKILL_BAR open: SWAP_HANDS + loot-chests: # Time in seconds it takes for a loot chest to @@ -241,6 +244,7 @@ resource-bar-colors: # false - Never verbose command-verbose: attribute: true + skill: true class: true experience: true level: true diff --git a/src/main/resources/default/classes/arcane-mage.yml b/src/main/resources/default/classes/arcane-mage.yml index 7dd9fa9b..3f1f657b 100644 --- a/src/main/resources/default/classes/arcane-mage.yml +++ b/src/main/resources/default/classes/arcane-mage.yml @@ -100,6 +100,11 @@ skills: level: 15 max-level: 30 + +#The number of skills a player can bound. +max-bound-skills: 6 + + # Experience sources for main class experience. main-exp-sources: - 'killmob{type=ZOMBIE;amount=1-3}' diff --git a/src/main/resources/default/classes/human.yml b/src/main/resources/default/classes/human.yml index 804904be..10b547d4 100644 --- a/src/main/resources/default/classes/human.yml +++ b/src/main/resources/default/classes/human.yml @@ -20,6 +20,9 @@ options: # Must match an existing exp curve filename from the 'expcurves' folder exp-curve: levels +#The number of skills a player can bound. +max-bound-skills: 5 + # Experience sources for main class experience. main-exp-sources: - 'killmob{type=ZOMBIE;amount=1-3}' diff --git a/src/main/resources/default/classes/mage.yml b/src/main/resources/default/classes/mage.yml index 322efc2b..9f9c6aaa 100644 --- a/src/main/resources/default/classes/mage.yml +++ b/src/main/resources/default/classes/mage.yml @@ -54,6 +54,8 @@ mana: cast-particle: particle: SPELL_INSTANT + + # Special resource regeneration: (when out of combat), # players can regen a set % of their maximum mana/missing mana. # This % can scale with the player level. @@ -140,6 +142,11 @@ skills: level: 15 max-level: 30 + + +#The number of skills a player can bound. +max-bound-skills: 6 + # Experience sources for main class experience. main-exp-sources: - 'killmob{type=ZOMBIE;amount=1-3}' diff --git a/src/main/resources/default/classes/marksman.yml b/src/main/resources/default/classes/marksman.yml index 64c6d2b4..4449115b 100644 --- a/src/main/resources/default/classes/marksman.yml +++ b/src/main/resources/default/classes/marksman.yml @@ -33,6 +33,8 @@ max-level: 100 exp-table: class_exp_table + + # Particles displayed around the player # when he enters the casting mode. cast-particle: @@ -85,6 +87,11 @@ attributes: base: .105 per-level: 0 + + +#The number of skills a player can bound. +max-bound-skills: 5 + # Experience sources for main class experience. main-exp-sources: - 'killmob{type=ZOMBIE;amount=1-3}' diff --git a/src/main/resources/default/classes/paladin.yml b/src/main/resources/default/classes/paladin.yml index 952f90f4..072415b6 100644 --- a/src/main/resources/default/classes/paladin.yml +++ b/src/main/resources/default/classes/paladin.yml @@ -30,6 +30,8 @@ display: # Must match an existing exp curve filename from the 'expcurves' folder exp-curve: levels + + # The maximum level players can reach max-level: 100 @@ -75,6 +77,11 @@ attributes: base: .095 per-level: 0 + + +#The number of skills a player can bound. +max-bound-skills: 5 + # Experience sources for main class experience. main-exp-sources: - 'killmob{type=ZOMBIE;amount=1-3}' diff --git a/src/main/resources/default/classes/rogue.yml b/src/main/resources/default/classes/rogue.yml index 85070e3e..8f78ec1a 100644 --- a/src/main/resources/default/classes/rogue.yml +++ b/src/main/resources/default/classes/rogue.yml @@ -33,6 +33,7 @@ max-level: 100 exp-table: class_exp_table + options: # Mana and health regen only applies when out of combat off-combat-mana-regen: true @@ -80,6 +81,10 @@ attributes: base: .105 per-level: 0 + +#The number of skills a player can bound. +max-bound-skills: 5 + # Experience sources for main class experience. main-exp-sources: - 'killmob{type=ZOMBIE;amount=1-3}' diff --git a/src/main/resources/default/classes/warrior.yml b/src/main/resources/default/classes/warrior.yml index dd10fb90..07d1e43a 100644 --- a/src/main/resources/default/classes/warrior.yml +++ b/src/main/resources/default/classes/warrior.yml @@ -116,6 +116,11 @@ attributes: base: 4.2 per-level: 0.05 + + +#The number of skills a player can bound. +max-bound-skills: 5 + # Experience sources for main class experience. main-exp-sources: - 'killmob{type=ZOMBIE;amount=1-3}' diff --git a/src/main/resources/default/gui/attribute-view.yml b/src/main/resources/default/gui/attribute-view.yml index 444a33e2..4722590d 100644 --- a/src/main/resources/default/gui/attribute-view.yml +++ b/src/main/resources/default/gui/attribute-view.yml @@ -2,6 +2,7 @@ # GUI display name name: Player Attributes + # Number of slots in your inventory. Must be # between 9 and 54 and must be a multiple of 9. slots: 27 @@ -11,70 +12,61 @@ items: slots: [26] function: reallocation item: CAULDRON - name: '&aReallocate Skill Points' + name: '&aAttribute Points' lore: - - '' - - 'You have spent a total of &6{total}&7 skill points.' - - '&7Right click to reallocate them.' - - '' - - '&eCosts 1 attribute reallocation point.' - - '&e◆ Attribute Reallocation Points: &6{points}' + - '' + - 'You have spent a total of &6{total}&7 attribute points.' + - '&7Right click to reallocate them.' + - '' + - '&eCosts 1 attribute reallocation point.' + - '&e◆ Attribute Reallocation Points: &6{points}' str: slots: [11] function: attribute_strength - shift-cost: 10 name: '&a{name}' item: GOLDEN_APPLE lore: # {buffs} returns amount of buffs - - '' - - '&7Points Spent: &6{spent}&7/&6{max}' - - '&7Current {name}: &6&l{current}' - - '' - - '&8When Leveled Up:' - - '&7 +{buff_weapon_damage}% Weapon Damage (&a+{total_weapon_damage}%&7)' - - '&7 +{buff_max_health}% Max Health (&a+{total_max_health}%&7)' - - '' - - '&eClick to level up for 1 attribute point.' - - '&eShift-Click to level up for {shift_points} attribute points.' - - '' - - '&e◆ Current Attribute Points: {attribute_points}' + - '' + - '&7Points Spent: &6{spent}&7/&6{max}' + - '&7Current {name}: &6&l{current}' + - '' + - '&8When Leveled Up:' + - '&7 +{buff_weapon_damage}% Weapon Damage (&a+{total_weapon_damage}%&7)' + - '&7 +{buff_max_health} Max Health (&a+{total_max_health}&7)' + - '' + - '&eClick to level up for 1 attribute point.' + - '&e◆ Current Attribute Points: {attribute_points}' dex: slots: [13] function: attribute_dexterity - shift-cost: 10 name: '&a{name}' item: LEATHER_BOOTS hide-flags: true lore: - - '' - - '&7Points Spent: &6{spent}&7/&6{max}' - - '&7Current {name}: &6&l{current}' - - '' - - '&8When Leveled Up:' - - '&7 +{buff_physical_damage}% Physical Damage (&a+{total_physical_damage}%&7)' - - '&7 +{buff_projectile_damage}% Projectile Damage (&a+{total_projectile_damage}%&7)' - - '&7 +{buff_attack_speed}% Attack Speed (&a+{total_attack_speed}&7)' - - '' - - '&eClick to level up for 1 attribute point.' - - '&eShift-Click to level up for {shift_points} attribute points.' - - '' - - '&e◆ Current Attribute Points: {attribute_points}' + - '' + - '&7Points Spent: &6{spent}&7/&6{max}' + - '&7Current {name}: &6&l{current}' + - '' + - '&8When Leveled Up:' + - '&7 +{buff_physical_damage}% Physical Damage (&a+{total_physical_damage}%&7)' + - '&7 +{buff_projectile_damage}% Projectile Damage (&a+{total_projectile_damage}%&7)' + - '&7 +{buff_attack_speed} Attack Speed (&a+{total_attack_speed}&7)' + - '' + - '&eClick to level up for 1 attribute point.' + - '&e◆ Current Attribute Points: {attribute_points}' int: slots: [15] function: attribute_intelligence - shift-cost: 10 name: '&a{name}' item: BOOK lore: - - '' - - '&7Points Spent: &6{spent}&7/&6{max}' - - '&7Current {name}: &6&l{current}' - - '' - - '&8When Leveled Up:' - - '&7 +{buff_magic_damage}% Magic Damage (&a+{total_magic_damage}%&7)' - - '&7 +{buff_cooldown_reduction}% Cooldown Reduction (&a+{total_cooldown_reduction}%&7)' - - '' - - '&eClick to level up for 1 attribute point.' - - '&eShift-Click to level up for {shift_points} attribute points.' - - '' - - '&e◆ Current Attribute Points: {attribute_points}' + - '' + - '&7Points Spent: &6{spent}&7/&6{max}' + - '&7Current {name}: &6&l{current}' + - '' + - '&8When Leveled Up:' + - '&7 +{buff_magic_damage}% Magic Damage (&a+{total_magic_damage}%&7)' + - '&7 +{buff_cooldown_reduction}% Cooldown Reduction (&a+{total_cooldown_reduction}%&7)' + - '' + - '&eClick to level up for 1 attribute point.' + - '&e◆ Current Attribute Points: {attribute_points}' diff --git a/src/main/resources/default/gui/skill-list.yml b/src/main/resources/default/gui/skill-list.yml index f3c5598c..8aeb0c00 100644 --- a/src/main/resources/default/gui/skill-list.yml +++ b/src/main/resources/default/gui/skill-list.yml @@ -33,6 +33,18 @@ items: name: '&aPrevious' lore: { } + reallocate: + slots: [26] + function: reallocation + item: CAULDRON + name: '&aReallocate Skill Points' + lore: + - '' + - 'You have spent a total of &6{total}&7 skill points.' + - '&7Right click to reallocate them.' + - '' + - '&eCosts 1 skill reallocation point.' + - '&e◆ Skill Reallocation Points: &6{points}' #switch: # @@ -87,11 +99,9 @@ items: upgrade: slots: [ 15 ] function: upgrade - shift-cost: 10 item: GREEN_STAINED_GLASS_PANE name: '&a&lUPGRADE {skill_caps}' lore: - '&7Costs 1 skill point.' - - '&7Shift-Click to spend {shift_points} points' - '' - '&eCurrent Skill Points: {skill_points}' diff --git a/src/main/resources/default/messages.yml b/src/main/resources/default/messages.yml index 8fd3a303..c5f53791 100644 --- a/src/main/resources/default/messages.yml +++ b/src/main/resources/default/messages.yml @@ -157,7 +157,7 @@ no-permission-for-class: # Attributes no-attribute-points-spent: '&cYou have not spent any attribute points.' not-attribute-reallocation-point: '&cYou do not have 1 reallocation point.' -not-attribute-point: '&cYou don''t have 1 attribute point.' +not-attribute-point: '&cYou have no attribute point.' not-attribute-point-shift: '&cYou must have &4{shift_points} &cattribute points.' attribute-points-reallocated: '&eYou successfully reset your attributes. You now have &6{points} &eattribute points.' attribute-max-points-hit: '&cYou cannot level up this attribute anymore.' @@ -173,8 +173,11 @@ no-skill-bound: '&cYou don''t have any skill bound to this slot.' not-active-skill: '&cThis is not an active skill.' skill-max-level-hit: '&cYou already hit the max level for that skill.' no-skill-placeholder: 'No Skill Bound' +not-skill-reallocation-point: '&cYou do not have 1 skill reallocation point.' +no-skill-points-spent: '&cYou have not spent any skill points.' +skill-points-reallocated: '&eYou successfully reset your attributes. You now have &6{points} &eskill points.' -#Skill Tree +# Skill Trees 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!' diff --git a/src/main/resources/default/sounds.yml b/src/main/resources/default/sounds.yml index 118b50a6..c2060944 100644 --- a/src/main/resources/default/sounds.yml +++ b/src/main/resources/default/sounds.yml @@ -19,6 +19,7 @@ select-class: UI_TOAST_CHALLENGE_COMPLETE level-attribute: ENTITY_PLAYER_LEVELUP reset-attributes: ENTITY_PLAYER_LEVELUP +reset-skills: ENTITY_PLAYER_LEVELUP level-skill-tree-node: ENTITY_PLAYER_LEVELUP reset-skill-tree: ENTITY_PLAYER_LEVELUP