diff --git a/MMOCore-API/src/main/java/net/Indyuce/mmocore/api/load/DefaultMMOLoader.java b/MMOCore-API/src/main/java/net/Indyuce/mmocore/api/load/DefaultMMOLoader.java index d29a0ff6..820b85a1 100644 --- a/MMOCore-API/src/main/java/net/Indyuce/mmocore/api/load/DefaultMMOLoader.java +++ b/MMOCore-API/src/main/java/net/Indyuce/mmocore/api/load/DefaultMMOLoader.java @@ -23,6 +23,9 @@ public class DefaultMMOLoader extends MMOLoader { if (config.getKey().equals("stat")) return new StatTrigger(config); + if(config.getKey().equals("unlock_slot")) + return new UnlockSlotTrigger(config); + if (config.getKey().equals("unlock_skill")) return new UnlockSkillTrigger(config); diff --git a/MMOCore-API/src/main/java/net/Indyuce/mmocore/api/player/PlayerData.java b/MMOCore-API/src/main/java/net/Indyuce/mmocore/api/player/PlayerData.java index 2ff55bf5..07e9391e 100644 --- a/MMOCore-API/src/main/java/net/Indyuce/mmocore/api/player/PlayerData.java +++ b/MMOCore-API/src/main/java/net/Indyuce/mmocore/api/player/PlayerData.java @@ -20,10 +20,11 @@ import net.Indyuce.mmocore.api.player.profess.PlayerClass; import net.Indyuce.mmocore.api.player.profess.SavedClassInformation; import net.Indyuce.mmocore.api.player.profess.Subclass; import net.Indyuce.mmocore.api.player.profess.resource.PlayerResource; -import net.Indyuce.mmocore.api.player.profess.skillbinding.BoundSkillInfo; +import net.Indyuce.mmocore.skill.binding.BoundSkillInfo; 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.SkillModifierTrigger; import net.Indyuce.mmocore.api.quest.trigger.StatTrigger; import net.Indyuce.mmocore.api.util.Closable; import net.Indyuce.mmocore.api.util.MMOCoreUtils; @@ -43,6 +44,7 @@ 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.SkillSlot; import net.Indyuce.mmocore.skill.cast.SkillCastingHandler; import net.Indyuce.mmocore.skilltree.IntegerCoordinates; import net.Indyuce.mmocore.skilltree.NodeStatus; @@ -145,8 +147,11 @@ public class PlayerData extends OfflinePlayerData implements Closable, Experienc this.mmoData = mmoData; questData = new PlayerQuests(this); playerStats = new PlayerStats(this); + + } + /** * Update all references after /mmocore reload so there can be garbage * collection with old plugin objects like class or skill instances. @@ -207,7 +212,7 @@ public class PlayerData extends OfflinePlayerData implements Closable, Experienc } public int countSkillTreePoints(SkillTree skillTree) { - return nodeLevels.keySet().stream().filter(node -> node.getTree().equals(skillTree)).mapToInt(nodeLevels::get).sum(); + return nodeLevels.keySet().stream().filter(node -> node.getTree().equals(skillTree)).mapToInt(node -> nodeLevels.get(node) * node.getSkillTreePointsConsumed()).sum(); } /** @@ -346,7 +351,7 @@ public class PlayerData extends OfflinePlayerData implements Closable, Experienc } public void setNodeLevel(SkillTreeNode node, int nodeLevel) { - int delta = (nodeLevel - nodeLevels.getOrDefault(node, 0))*node.getSkillTreePointsConsumed(); + int delta = (nodeLevel - nodeLevels.getOrDefault(node, 0)) * node.getSkillTreePointsConsumed(); pointSpent.put(node.getTree(), pointSpent.getOrDefault(node.getTree(), 0) + delta); nodeLevels.put(node, nodeLevel); } @@ -378,22 +383,23 @@ public class PlayerData extends OfflinePlayerData implements Closable, Experienc return result; } - - /** * @return If the item is unlocked by the player - * This is used for skills that can be locked & unlocked. - *

- * Looks at the real value and thus remove the plugin identifier + * This is used for skills that can be locked & unlocked. + *

+ * Looks at the real value and thus remove the plugin identifier */ public boolean hasUnlocked(Unlockable unlockable) { - String unlockableKey = unlockable.getUnlockNamespacedKey().substring(unlockable.getUnlockNamespacedKey().indexOf(":")); - return unlockedItems - .stream() - .filter(key -> key.substring(key.indexOf(":")).equals(unlockableKey)) - .collect(Collectors.toList()).size() != 0; + return hasUnlocked(unlockable.getUnlockNamespacedKey()); + } + public boolean hasUnlocked(String unlockNamespacedKey) { + return unlockedItems + .stream() + .filter(key -> key.equals(unlockNamespacedKey)) + .collect(Collectors.toList()).size() != 0; + } /** * Unlocks an item for the player. This is mainly used to unlock skills. @@ -1176,12 +1182,27 @@ public class PlayerData extends OfflinePlayerData implements Closable, Experienc if (isOnline()) getStats().updateStats(); - if (profess != null) + if (profess != null) { + // Loads the classUnlockedSkills profess.getSkills() .stream() .filter(ClassSkill::isUnlockedByDefault) .forEach(skill -> unlock(skill.getSkill())); + + // Loads the classUnlockedSkills + profess.getSkills() + .stream() + .filter(ClassSkill::isUnlockedByDefault) + .forEach(skill -> unlock(skill.getSkill())); + + // Loads the classUnlockedSlots + profess.getSlots() + .stream() + .filter(SkillSlot::isUnlockedByDefault) + .forEach(this::unlock); + } + } public boolean hasSkillBound(int slot) { @@ -1208,9 +1229,15 @@ public class PlayerData extends OfflinePlayerData implements Closable, Experienc public void bindSkill(int slot, ClassSkill skill) { Validate.notNull(skill, "Skill cannot be null"); //Unbinds the previous skill (Important for passive skills. + String skillId = skill.getSkill().getHandler().getId(); if (boundSkills.containsKey(slot)) boundSkills.get(slot).unbind(); if (slot >= 0) { + //We apply the skill buffs associated with the slot to the skill. + for (SkillModifierTrigger skillBuffTrigger : profess.getSkillSlot(slot).getSkillBuffTriggers()) + if (skillBuffTrigger.getTargetSkills().contains(skillId)) + skillBuffTrigger.apply(this, skill.getSkill().getHandler()); + if (skill.getSkill().getTrigger().isPassive()) { PassiveSkill passiveSkill = skill.toPassive(this); passiveSkill.register(mmoData); @@ -1222,6 +1249,11 @@ public class PlayerData extends OfflinePlayerData implements Closable, Experienc } public void unbindSkill(int slot) { + + // We remove the skill buffs associated with the slot from the skill that is . + profess.getSkillSlot(slot).getSkillBuffTriggers().forEach(skillBuffTrigger -> + skillBuffTrigger.remove(this, boundSkills.get(slot).getClassSkill().getSkill().getHandler())); + BoundSkillInfo boundSkillInfo = boundSkills.remove(slot); boundSkillInfo.unbind(); } @@ -1245,7 +1277,7 @@ public class PlayerData extends OfflinePlayerData implements Closable, Experienc * checks if they could potentially upgrade to one of these * * @return If the player can change its current class to - * a subclass + * a subclass */ @Deprecated public boolean canChooseSubclass() { diff --git a/MMOCore-API/src/main/java/net/Indyuce/mmocore/api/player/profess/PlayerClass.java b/MMOCore-API/src/main/java/net/Indyuce/mmocore/api/player/profess/PlayerClass.java index 5632bde9..94f6dd5b 100644 --- a/MMOCore-API/src/main/java/net/Indyuce/mmocore/api/player/profess/PlayerClass.java +++ b/MMOCore-API/src/main/java/net/Indyuce/mmocore/api/player/profess/PlayerClass.java @@ -21,7 +21,7 @@ import net.Indyuce.mmocore.api.player.profess.event.EventTrigger; import net.Indyuce.mmocore.api.player.profess.resource.ManaDisplayOptions; import net.Indyuce.mmocore.api.player.profess.resource.PlayerResource; import net.Indyuce.mmocore.api.player.profess.resource.ResourceRegeneration; -import net.Indyuce.mmocore.api.player.profess.skillbinding.SkillSlot; +import net.Indyuce.mmocore.skill.binding.SkillSlot; import net.Indyuce.mmocore.api.util.MMOCoreUtils; import net.Indyuce.mmocore.api.util.math.formula.LinearValue; import net.Indyuce.mmocore.experience.EXPSource; @@ -185,15 +185,13 @@ public class PlayerClass extends PostLoadObject implements ExperienceObject { } // Skill slots - if (config.contains("skill-slots")) - for (String key : config.getConfigurationSection("skill-slots").getKeys(false)) - try { - SkillSlot skillSlot = new SkillSlot(config.getConfigurationSection("skill-slots." + key)); - skillSlots.put(skillSlot.getSlot(), skillSlot); - } catch (RuntimeException exception) { - MMOCore.plugin.getLogger().log(Level.WARNING, "Could not load skill slot '" + key + "' from class '" - + id + "': " + exception.getMessage()); - } + Validate.isTrue(config.isConfigurationSection("skill-slots"), "You must define the skills-slots for class " + id); + for (int i = 1; i < MMOCore.plugin.configManager.maxSlots + 1; i++) { + if (config.contains("skill-slots." + i)) + skillSlots.put(i, new SkillSlot(config.getConfigurationSection("skill-slots." + i))); + else + skillSlots.put(i, new SkillSlot(i, 0, "true", "&eSkill Slot " + MMOCoreUtils.intToRoman(i), new ArrayList<>(), false, true, new ArrayList<>())); + } // Class skills for (RegisteredSkill registered : MMOCore.plugin.skillManager.getAll()) { @@ -201,7 +199,7 @@ public class PlayerClass extends PostLoadObject implements ExperienceObject { if (config.contains("skills." + key)) skills.put(key, new ClassSkill(registered, config.getConfigurationSection("skills." + key))); else - skills.put(key, new ClassSkill(registered, 1, 1,false)); + skills.put(key, new ClassSkill(registered, 1, 1, false)); } // Casting particle @@ -446,6 +444,10 @@ public class PlayerClass extends PostLoadObject implements ExperienceObject { return skillSlots.get(slot); } + public Collection getSlots() { + return skillSlots.values(); + } + public ClassSkill getSkill(RegisteredSkill skill) { return getSkill(skill.getHandler().getId()); } diff --git a/MMOCore-API/src/main/java/net/Indyuce/mmocore/api/player/profess/SavedClassInformation.java b/MMOCore-API/src/main/java/net/Indyuce/mmocore/api/player/profess/SavedClassInformation.java index 05881478..b307c634 100644 --- a/MMOCore-API/src/main/java/net/Indyuce/mmocore/api/player/profess/SavedClassInformation.java +++ b/MMOCore-API/src/main/java/net/Indyuce/mmocore/api/player/profess/SavedClassInformation.java @@ -7,6 +7,7 @@ import net.Indyuce.mmocore.api.player.PlayerData; import net.Indyuce.mmocore.api.player.attribute.PlayerAttribute; import net.Indyuce.mmocore.api.util.MMOCoreUtils; import net.Indyuce.mmocore.player.ClassDataContainer; +import net.Indyuce.mmocore.skill.ClassSkill; import net.Indyuce.mmocore.skill.RegisteredSkill; import net.Indyuce.mmocore.skilltree.SkillTreeNode; import net.Indyuce.mmocore.skilltree.tree.SkillTree; @@ -86,6 +87,7 @@ public class SavedClassInformation { mana = json.has("mana") ? json.get("mana").getAsDouble() : 0; stamina = json.has("stamina") ? json.get("stamina").getAsDouble() : 0; stellium = json.has("stellium") ? json.get("stellium").getAsDouble() : 0; + if (json.has("attribute")) for (Entry entry : json.getAsJsonObject("attribute").entrySet()) attributeLevels.put(entry.getKey(), entry.getValue().getAsInt()); diff --git a/MMOCore-API/src/main/java/net/Indyuce/mmocore/api/player/profess/skillbinding/SkillSlot.java b/MMOCore-API/src/main/java/net/Indyuce/mmocore/api/player/profess/skillbinding/SkillSlot.java deleted file mode 100644 index 3483ae41..00000000 --- a/MMOCore-API/src/main/java/net/Indyuce/mmocore/api/player/profess/skillbinding/SkillSlot.java +++ /dev/null @@ -1,63 +0,0 @@ -package net.Indyuce.mmocore.api.player.profess.skillbinding; - -import net.Indyuce.mmocore.skill.ClassSkill; -import org.bukkit.Material; -import org.bukkit.configuration.ConfigurationSection; - -import javax.annotation.Nullable; -import java.util.List; - -public class SkillSlot { - private final int slot, modelData; - private final String formula; - private final String name; - private final List lore; - private final Material item; - - public SkillSlot(int slot, int modelData, String formula, String name, List lore, Material item) { - this.slot = slot; - this.modelData = modelData; - this.formula = formula; - this.name = name; - this.lore = lore; - this.item = item; - } - - public SkillSlot(ConfigurationSection section) { - this.slot = Integer.parseInt(section.getName()); - this.formula = section.contains("expression") ? section.getString("expression") : "true"; - this.name = section.getString("name"); - this.lore = section.getStringList("lore"); - this.item = section.contains("item") ? Material.valueOf(section.getString("item")) : null; - this.modelData = section.getInt("model-data", 0); - } - - public int getSlot() { - return slot; - } - - public String getName() { - return name; - } - - public List getLore() { - return lore; - } - - @Nullable - public Material getItem() { - return item; - } - - public boolean hasItem() { - return item != null; - } - - public int getModelData() { - return modelData; - } - - public boolean acceptsSkill(ClassSkill classSkill) { - return classSkill.getSkill().matchesFormula(formula); - } -} diff --git a/MMOCore-API/src/main/java/net/Indyuce/mmocore/api/quest/trigger/SkillModifierTrigger.java b/MMOCore-API/src/main/java/net/Indyuce/mmocore/api/quest/trigger/SkillModifierTrigger.java index 0ec0ec71..0d2903ad 100644 --- a/MMOCore-API/src/main/java/net/Indyuce/mmocore/api/quest/trigger/SkillModifierTrigger.java +++ b/MMOCore-API/src/main/java/net/Indyuce/mmocore/api/quest/trigger/SkillModifierTrigger.java @@ -1,12 +1,14 @@ package net.Indyuce.mmocore.api.quest.trigger; import io.lumine.mythic.lib.api.MMOLineConfig; +import io.lumine.mythic.lib.player.modifier.ModifierType; import io.lumine.mythic.lib.player.skillmod.SkillModifier; import io.lumine.mythic.lib.skill.handler.SkillHandler; import net.Indyuce.mmocore.MMOCore; import net.Indyuce.mmocore.api.player.PlayerData; import net.Indyuce.mmocore.api.quest.trigger.api.Removable; import net.Indyuce.mmocore.skill.RegisteredSkill; +import org.apache.commons.lang.Validate; import java.util.ArrayList; import java.util.List; @@ -14,7 +16,7 @@ import java.util.UUID; public class SkillModifierTrigger extends Trigger implements Removable { private final SkillModifier mod; - private final String modifierKey = TRIGGER_PREFIX + "." + UUID.randomUUID(); + private final String buffKey = TRIGGER_PREFIX + "." + UUID.randomUUID(); private final double amount; public SkillModifierTrigger(MMOLineConfig config) { @@ -22,18 +24,22 @@ public class SkillModifierTrigger extends Trigger implements Removable { config.validateKeys("modifier"); config.validateKeys("amount"); - config.validateKeys("formula"); config.validateKeys("type"); - amount = config.getDouble("amount"); String skillModifier = config.getString("modifier"); - String formula = config.getString("formula"); - final List> targetSkills = new ArrayList<>(); - for (RegisteredSkill skill : MMOCore.plugin.skillManager.getAll()) + String formula = config.getString("formula", "true"); + String type = config.getString("type").toUpperCase(); + Validate.isTrue(type.equals("FLAT") || type.equals("RELATIVE")); + List> targetSkills = new ArrayList<>(); + for (RegisteredSkill skill : MMOCore.plugin.skillManager.getAll()) { if (skill.matchesFormula(formula)) targetSkills.add(skill.getHandler()); + } + mod = new SkillModifier(buffKey, skillModifier, targetSkills, amount, ModifierType.valueOf(type)); + } - mod = new SkillModifier(modifierKey, skillModifier, targetSkills, amount); + public List> getTargetSkills() { + return mod.getSkills(); } @Override @@ -45,4 +51,18 @@ public class SkillModifierTrigger extends Trigger implements Removable { public void remove(PlayerData playerData) { mod.unregister(playerData.getMMOPlayerData()); } + + /** + * Used by skill slots to apply a skillBuff to a specific skill dynamically chosen . + */ + public void apply(PlayerData playerData, SkillHandler skill) { + mod.register(playerData.getMMOPlayerData(), skill); + } + + /** + * Used by skill slots to remove a skillBuff from a specific skill dynamically chosen. + */ + public void remove(PlayerData playerData, SkillHandler skill) { + mod.unregister(playerData.getMMOPlayerData(), skill); + } } diff --git a/MMOCore-API/src/main/java/net/Indyuce/mmocore/api/quest/trigger/UnlockSlotTrigger.java b/MMOCore-API/src/main/java/net/Indyuce/mmocore/api/quest/trigger/UnlockSlotTrigger.java new file mode 100644 index 00000000..3f074498 --- /dev/null +++ b/MMOCore-API/src/main/java/net/Indyuce/mmocore/api/quest/trigger/UnlockSlotTrigger.java @@ -0,0 +1,34 @@ +package net.Indyuce.mmocore.api.quest.trigger; + +import io.lumine.mythic.lib.api.MMOLineConfig; +import net.Indyuce.mmocore.MMOCore; +import net.Indyuce.mmocore.api.player.PlayerData; +import net.Indyuce.mmocore.skill.binding.SkillSlot; +import net.Indyuce.mmocore.api.quest.trigger.api.Removable; +import org.apache.commons.lang.Validate; + + +public class UnlockSlotTrigger extends Trigger implements Removable { + private final int slot; + + public UnlockSlotTrigger(MMOLineConfig config) { + super(config); + config.validateKeys("slot"); + slot = Integer.parseInt("slot"); + Validate.isTrue(slot > 0 && slot <= MMOCore.plugin.configManager.maxSlots, "The slot should be between 1 and " + MMOCore.plugin.configManager.maxSlots); + } + + @Override + public void apply(PlayerData player) { + SkillSlot skillSlot = player.getProfess().getSkillSlot(slot); + if (!player.hasUnlocked(skillSlot)) + player.unlock(skillSlot); + } + + @Override + public void remove(PlayerData player) { + SkillSlot skillSlot = player.getProfess().getSkillSlot(slot); + if (player.hasUnlocked(skillSlot)) + player.lock(skillSlot); + } +} diff --git a/MMOCore-API/src/main/java/net/Indyuce/mmocore/command/rpg/admin/AdminCommandTreeNode.java b/MMOCore-API/src/main/java/net/Indyuce/mmocore/command/rpg/admin/AdminCommandTreeNode.java index a1cc45dd..18a444b3 100644 --- a/MMOCore-API/src/main/java/net/Indyuce/mmocore/command/rpg/admin/AdminCommandTreeNode.java +++ b/MMOCore-API/src/main/java/net/Indyuce/mmocore/command/rpg/admin/AdminCommandTreeNode.java @@ -22,14 +22,14 @@ public class AdminCommandTreeNode extends CommandTreeNode { addChild(new AttributeCommandTreeNode(this)); addChild(new SkillCommandTreeNode(this)); addChild(new SaveDataTreeNode(this)); - + addChild(new SlotCommandTreeNode(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-realloc", this, PlayerData::setSkillReallocationPoints, PlayerData::giveSkillReallocationPoints, PlayerData::getSkillReallocationPoints)); addChild(new PointsCommandTreeNode("skill-tree-realloc", this, PlayerData::setSkillTreeReallocationPoints, PlayerData::giveSkillTreeReallocationPoints, PlayerData::getSkillTreeReallocationPoints)); - addChild(new SkillTreePointsCommandTreeNode(this,(playerData, integer, s) -> playerData.setSkillTreePoints(s,integer),(playerData, integer, s) -> playerData.giveSkillTreePoints(s,integer),((playerData, s) -> playerData.getSkillTreePoint(s)))); + addChild(new SkillTreePointsCommandTreeNode(this, (playerData, integer, s) -> playerData.setSkillTreePoints(s, integer), (playerData, integer, s) -> playerData.giveSkillTreePoints(s, integer), ((playerData, s) -> playerData.getSkillTreePoint(s)))); for (PlayerResource res : PlayerResource.values()) addChild(new ResourceCommandTreeNode(res.name().toLowerCase(), this, res)); } diff --git a/MMOCore-API/src/main/java/net/Indyuce/mmocore/command/rpg/admin/SlotCommandTreeNode.java b/MMOCore-API/src/main/java/net/Indyuce/mmocore/command/rpg/admin/SlotCommandTreeNode.java new file mode 100644 index 00000000..f7fd32fe --- /dev/null +++ b/MMOCore-API/src/main/java/net/Indyuce/mmocore/command/rpg/admin/SlotCommandTreeNode.java @@ -0,0 +1,155 @@ +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.skill.binding.SkillSlot; +import net.Indyuce.mmocore.command.api.CommandVerbose; +import net.Indyuce.mmocore.skill.ClassSkill; +import org.bukkit.Bukkit; +import org.bukkit.ChatColor; +import org.bukkit.command.CommandSender; +import org.bukkit.entity.Player; + + +public class SlotCommandTreeNode extends CommandTreeNode { + public SlotCommandTreeNode(CommandTreeNode parent) { + super(parent, "slot"); + addChild(new LockSlotCommandTreeNode(this, "lock", true)); + addChild(new LockSlotCommandTreeNode(this, "unlock", false)); + addChild(new UnbindSlotCommandTreeNode(this, "unbind")); + addChild(new BindSlotCommandTreeNode(this, "bind")); + } + + public class LockSlotCommandTreeNode extends CommandTreeNode { + private final boolean lock; + + public LockSlotCommandTreeNode(CommandTreeNode parent, String id, boolean lock) { + super(parent, id); + this.lock = lock; + addParameter(Parameter.PLAYER); + addParameter(Parameter.AMOUNT); + } + + @Override + public CommandResult execute(CommandSender sender, String[] args) { + if (args.length < 5) + 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); + int slot; + try { + slot = Integer.parseInt(args[4]); + } catch (NumberFormatException e) { + sender.sendMessage(ChatColor.RED + args[4] + " is not a valid number."); + return CommandResult.FAILURE; + } + if (slot <= 0) { + sender.sendMessage(ChatColor.RED + "The slot can't be negative."); + return CommandResult.FAILURE; + } + SkillSlot skillSlot = playerData.getProfess().getSkillSlot(slot); + 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()); + return CommandResult.SUCCESS; + } + playerData.lock(skillSlot); + + } else { + if (playerData.hasUnlocked(skillSlot)) { + CommandVerbose.verbose(sender, CommandVerbose.CommandType.SKILL, ChatColor.RED + "The skill slot " + skillSlot.getName() + " is already unlocked" + " for " + player.getName()); + return CommandResult.SUCCESS; + } + playerData.unlock(skillSlot); + } + CommandVerbose.verbose(sender, CommandVerbose.CommandType.SKILL, ChatColor.GOLD + "The skill slot " + skillSlot.getName() + " is now " + (lock ? "locked" : "unlocked" + " for " + player.getName())); + return CommandResult.SUCCESS; + } + } + + + public class BindSlotCommandTreeNode extends CommandTreeNode { + + public BindSlotCommandTreeNode(CommandTreeNode parent, String id) { + super(parent, id); + addParameter(Parameter.PLAYER); + addParameter(Parameter.AMOUNT); + addParameter(new Parameter("", + (explorer, list) -> MMOCore.plugin.skillManager.getAll().forEach(skill -> list.add(skill.getHandler().getId().toUpperCase())))); + + } + + @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); + int slot; + try { + slot = Integer.parseInt(args[4]); + } catch (NumberFormatException e) { + sender.sendMessage(ChatColor.RED + args[4] + " is not a valid number."); + return CommandResult.FAILURE; + } + ClassSkill skill = playerData.getProfess().getSkill(args[5]); + if (skill == null) { + sender.sendMessage(ChatColor.RED + "The player's class doesn't have a skill called " + args[5] + "."); + return CommandResult.FAILURE; + } + playerData.bindSkill(slot, skill); + + CommandVerbose.verbose(sender, CommandVerbose.CommandType.SKILL, ChatColor.GOLD + "The skill " + skill.getSkill().getHandler().getId() + " is now bound to the slot " + slot); + return CommandResult.SUCCESS; + } + } + + public class UnbindSlotCommandTreeNode extends CommandTreeNode { + + public UnbindSlotCommandTreeNode(CommandTreeNode parent, String id) { + super(parent, id); + addParameter(Parameter.PLAYER); + addParameter(Parameter.AMOUNT); + } + + @Override + public CommandResult execute(CommandSender sender, String[] args) { + if (args.length < 5) + 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); + int slot; + try { + slot = Integer.parseInt(args[4]); + } catch (NumberFormatException e) { + sender.sendMessage(ChatColor.RED + args[4] + " is not a valid number."); + return CommandResult.FAILURE; + } + String skill = playerData.hasSkillBound(slot) ? playerData.getBoundSkill(slot).getSkill().getHandler().getId() : "none"; + if (playerData.hasSkillBound(slot)) + playerData.unbindSkill(slot); + + CommandVerbose.verbose(sender, CommandVerbose.CommandType.SKILL, ChatColor.GOLD + "The skill " + skill + " has been unbounded from the slot " + slot); + return CommandResult.SUCCESS; + } + } + + @Override + public CommandResult execute(CommandSender sender, String[] args) { + return CommandResult.THROW_USAGE; + } +} diff --git a/MMOCore-API/src/main/java/net/Indyuce/mmocore/gui/SkillList.java b/MMOCore-API/src/main/java/net/Indyuce/mmocore/gui/SkillList.java index 09df079d..ee71e003 100644 --- a/MMOCore-API/src/main/java/net/Indyuce/mmocore/gui/SkillList.java +++ b/MMOCore-API/src/main/java/net/Indyuce/mmocore/gui/SkillList.java @@ -4,8 +4,9 @@ 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.api.player.PlayerData; -import net.Indyuce.mmocore.api.player.profess.skillbinding.SkillSlot; +import net.Indyuce.mmocore.skill.binding.SkillSlot; import net.Indyuce.mmocore.api.util.MMOCoreUtils; import net.Indyuce.mmocore.gui.api.EditableInventory; import net.Indyuce.mmocore.gui.api.GeneratedInventory; @@ -15,7 +16,6 @@ import net.Indyuce.mmocore.gui.api.item.Placeholders; import net.Indyuce.mmocore.gui.api.item.SimplePlaceholderItem; import net.Indyuce.mmocore.skill.ClassSkill; import net.Indyuce.mmocore.skill.RegisteredSkill; -import net.Indyuce.mmocore.api.SoundEvent; import org.apache.commons.lang.Validate; import org.bukkit.ChatColor; import org.bukkit.Material; @@ -27,6 +27,7 @@ import org.bukkit.inventory.ItemStack; import org.bukkit.inventory.meta.ItemMeta; import java.util.ArrayList; +import java.util.Comparator; import java.util.List; import java.util.logging.Level; import java.util.stream.Collectors; @@ -64,49 +65,7 @@ public class SkillList extends EditableInventory { } if (function.equals("slot")) - return new SlotItem(config) { - @Override - public ItemStack display(SkillViewerInventory inv, int n) { - if (!inv.getPlayerData().getProfess().hasSlot(n+1)) { - return new ItemStack(Material.AIR); - } - SkillSlot skillSlot = inv.getPlayerData().getProfess().getSkillSlot(n+1); - ItemStack item = super.display(inv, n); - if (!inv.getPlayerData().hasSkillBound(n+1)) { - //If there is an item filled in the slot config it shows it, else shows the default item. - Material material = skillSlot.hasItem() ? skillSlot.getItem() : super.emptyMaterial; - int customModelData = skillSlot.hasItem() ? skillSlot.getModelData() : super.emptyCMD; - item.setType(material); - ItemMeta meta = item.getItemMeta(); - meta.setDisplayName(MMOCore.plugin.placeholderParser.parse(inv.getPlayerData().getPlayer(),skillSlot.getName())); - List lore=skillSlot.getLore() - .stream() - .map(str->MMOCore.plugin.placeholderParser.parse(inv.getPlayerData().getPlayer(),str)) - .collect(Collectors.toList()); - meta.setLore(lore); - if (MythicLib.plugin.getVersion().isStrictlyHigher(1, 13)) { - meta.setCustomModelData(customModelData); - } - item.setItemMeta(meta); - - } - - return item; - } - - /** - * This should only be called when there is a skill bound. - */ - @Override - public Placeholders getPlaceholders(SkillViewerInventory inv, int n) { - Placeholders holders = super.getPlaceholders(inv, n); - String none = MythicLib.plugin.parseColors(config.getString("no-skill")); - RegisteredSkill skill = inv.getPlayerData().hasSkillBound(n+1) ? inv.getPlayerData().getBoundSkill(n+1).getSkill() : null; - holders.register("skill", skill == null ? none : skill.getName()); - return holders; - } - - }; + return new SlotItem(config); if (function.equals("previous")) return new SimplePlaceholderItem(config) { @@ -198,16 +157,49 @@ public class SkillList extends EditableInventory { emptyCMD = config.getInt("empty-custom-model-data", getModelData()); } + @Override + public ItemStack display(SkillViewerInventory inv, int n) { + if (!inv.getPlayerData().hasUnlocked("slot:" + (n+1))) { + return new ItemStack(Material.AIR); + } + SkillSlot skillSlot = inv.getPlayerData().getProfess().getSkillSlot(n + 1); + ItemStack item = super.display(inv, n); + if (!inv.getPlayerData().hasSkillBound(n + 1)) { + //If there is an item filled in the slot config it shows it, else shows the default item. + Material material = skillSlot.hasItem() ? skillSlot.getItem() : emptyMaterial; + int customModelData = skillSlot.hasItem() ? skillSlot.getModelData() : emptyCMD; + item.setType(material); + ItemMeta meta = item.getItemMeta(); + meta.setDisplayName(MMOCore.plugin.placeholderParser.parse(inv.getPlayerData().getPlayer(), skillSlot.getName())); + List lore = skillSlot.getLore() + .stream() + .map(str -> MMOCore.plugin.placeholderParser.parse(inv.getPlayerData().getPlayer(), str)) + .collect(Collectors.toList()); + meta.setLore(lore); + if (MythicLib.plugin.getVersion().isStrictlyHigher(1, 13)) { + meta.setCustomModelData(customModelData); + } + item.setItemMeta(meta); + + } + + 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 == null ? null : inv.selected.getSkill(); + RegisteredSkill selected = inv.selected.getSkill(); Placeholders holders = new Placeholders(); holders.register("index", "" + (n + 1)); holders.register("slot", MMOCoreUtils.intToRoman(n + 1)); 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()); return holders; } @@ -315,7 +307,7 @@ public class SkillList extends EditableInventory { private final List skillSlots; private final List slotSlots; - //The skill the player Selected + // Skill the player selected private ClassSkill selected; private int page = 0; @@ -323,9 +315,11 @@ public class SkillList extends EditableInventory { super(playerData, editable); skills = playerData.getProfess().getSkills() .stream() - .filter((classSkill)->playerData.hasUnlocked(classSkill.getSkill())) + .filter((classSkill) -> playerData.hasUnlocked(classSkill.getSkill())) + .sorted(Comparator.comparingInt(ClassSkill::getUnlockLevel)) .collect(Collectors.toList()); skillSlots = getEditable().getByFunction("skill").getSlots(); + Validate.notNull(getEditable().getByFunction("slot"), "Your skill GUI config file is out-of-date, please regenerate it."); slotSlots = getEditable().getByFunction("slot").getSlots(); selected = skills.get(page * skillSlots.size()); @@ -396,8 +390,14 @@ public class SkillList extends EditableInventory { * binding or unbinding skills. */ if (item.getFunction().equals("slot")) { - int index = slotSlots.indexOf(context.getSlot())+1; - SkillSlot skillSlot=playerData.getProfess().getSkillSlot(index); + int index = slotSlots.indexOf(context.getSlot()) + 1; + SkillSlot skillSlot = playerData.getProfess().getSkillSlot(index); + //Select if the player is doing Shift Left Click + if(context.getClickType() ==ClickType.SHIFT_LEFT){ + if(playerData.hasSkillBound(index)) + selected=playerData.getBoundSkill(index); + return; + } // unbind if there is a current spell. if (context.getClickType() == ClickType.RIGHT) { if (!playerData.hasSkillBound(index)) { @@ -405,22 +405,30 @@ public class SkillList extends EditableInventory { player.playSound(player.getLocation(), Sound.ENTITY_VILLAGER_NO, 1, 2); return; } + if(!playerData.getProfess().getSkillSlot(index).canManuallyBind()){ + MMOCore.plugin.configManager.getSimpleMessage("cant-manually-bind").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(); return; } - if (selected == null) - return; - if (!playerData.hasSkillUnlocked(selected)) { MMOCore.plugin.configManager.getSimpleMessage("not-unlocked-skill").send(player); player.playSound(player.getLocation(), Sound.ENTITY_VILLAGER_NO, 1, 2); return; } - if(!skillSlot.acceptsSkill(selected)){ + if (!skillSlot.canManuallyBind()){ + MMOCore.plugin.configManager.getSimpleMessage("cant-manually-bind").send(player); + player.playSound(player.getLocation(), Sound.ENTITY_VILLAGER_NO, 1, 2); + return; + } + + if (!skillSlot.acceptsSkill(selected)){ MMOCore.plugin.configManager.getSimpleMessage("not-compatible-skill").send(player); player.playSound(player.getLocation(), Sound.ENTITY_VILLAGER_NO, 1, 2); return; diff --git a/MMOCore-API/src/main/java/net/Indyuce/mmocore/manager/ConfigManager.java b/MMOCore-API/src/main/java/net/Indyuce/mmocore/manager/ConfigManager.java index eab332ab..1f87638b 100644 --- a/MMOCore-API/src/main/java/net/Indyuce/mmocore/manager/ConfigManager.java +++ b/MMOCore-API/src/main/java/net/Indyuce/mmocore/manager/ConfigManager.java @@ -34,7 +34,7 @@ public class ConfigManager { public long combatLogTimer, lootChestExpireTime, lootChestPlayerCooldown, globalSkillCooldown; public double lootChestsChanceWeight, dropItemsChanceWeight, fishingDropsChanceWeight, partyMaxExpSplitRange, pvpModeToggleOnCooldown, pvpModeToggleOffCooldown, pvpModeCombatCooldown, pvpModeCombatTimeout, pvpModeInvulnerabilityTimeRegionChange, pvpModeInvulnerabilityTimeCommand, pvpModeRegionEnterCooldown, pvpModeRegionLeaveCooldown; - public int maxPartyLevelDifference, maxBoundActiveSkills, maxBoundPassiveSkills, minCombatLevel, maxCombatLevelDifference; + public int maxPartyLevelDifference, maxSlots, minCombatLevel, maxCombatLevelDifference; public final List combatLogDamageCauses = new ArrayList<>(); private final FileConfiguration messages; @@ -157,8 +157,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"); - maxBoundActiveSkills = MMOCore.plugin.getConfig().getInt("max-bound-active-skills", 6); - maxBoundPassiveSkills = MMOCore.plugin.getConfig().getInt("max-bound-passive-skills", 3); + maxSlots = MMOCore.plugin.getConfig().getInt("max-slots"); overrideVanillaExp = MMOCore.plugin.getConfig().getBoolean("override-vanilla-exp"); } diff --git a/MMOCore-API/src/main/java/net/Indyuce/mmocore/manager/data/mysql/MMOCoreDataSynchronizer.java b/MMOCore-API/src/main/java/net/Indyuce/mmocore/manager/data/mysql/MMOCoreDataSynchronizer.java index 10e87a87..712f4a18 100644 --- a/MMOCore-API/src/main/java/net/Indyuce/mmocore/manager/data/mysql/MMOCoreDataSynchronizer.java +++ b/MMOCore-API/src/main/java/net/Indyuce/mmocore/manager/data/mysql/MMOCoreDataSynchronizer.java @@ -71,9 +71,11 @@ public class MMOCoreDataSynchronizer extends DataSynchronizer { } data.setupSkillTree(); Set unlockedItems = new HashSet<>(); - JsonArray unlockedItemsArray = new JsonParser().parse(result.getString("unlocked_items")).getAsJsonArray(); - for (JsonElement item : unlockedItemsArray) - unlockedItems.add(item.getAsString()); + if (!isEmpty(result.getString("unlocked_items"))) { + JsonArray unlockedItemsArray = new JsonParser().parse(result.getString("unlocked_items")).getAsJsonArray(); + for (JsonElement item : unlockedItemsArray) + unlockedItems.add(item.getAsString()); + } data.setUnlockedItems(unlockedItems); if (!isEmpty(result.getString("guild"))) { Guild guild = MMOCore.plugin.dataProvider.getGuildManager().getGuild(result.getString("guild")); diff --git a/MMOCore-API/src/main/java/net/Indyuce/mmocore/manager/data/yaml/YAMLPlayerDataManager.java b/MMOCore-API/src/main/java/net/Indyuce/mmocore/manager/data/yaml/YAMLPlayerDataManager.java index 317cdd40..64745961 100644 --- a/MMOCore-API/src/main/java/net/Indyuce/mmocore/manager/data/yaml/YAMLPlayerDataManager.java +++ b/MMOCore-API/src/main/java/net/Indyuce/mmocore/manager/data/yaml/YAMLPlayerDataManager.java @@ -6,6 +6,7 @@ import net.Indyuce.mmocore.api.player.OfflinePlayerData; import net.Indyuce.mmocore.api.player.PlayerData; import net.Indyuce.mmocore.api.player.profess.PlayerClass; import net.Indyuce.mmocore.api.player.profess.SavedClassInformation; +import net.Indyuce.mmocore.api.util.MMOCoreUtils; import net.Indyuce.mmocore.guild.provided.Guild; import net.Indyuce.mmocore.manager.data.DataProvider; import net.Indyuce.mmocore.manager.data.PlayerDataManager; @@ -80,10 +81,7 @@ public class YAMLPlayerDataManager extends PlayerDataManager { if (config.contains("times-claimed")) - for ( - String key : config.getConfigurationSection("times-claimed"). - - getKeys(false)) { + for (String key : config.getConfigurationSection("times-claimed").getKeys(false)) { ConfigurationSection section = config.getConfigurationSection("times-claimed." + key); if (section != null) for (String key1 : section.getKeys(false)) { @@ -97,8 +95,7 @@ public class YAMLPlayerDataManager extends PlayerDataManager { } data.setUnlockedItems(config.getStringList("unlocked-items").stream().collect(Collectors.toSet())); - for ( - SkillTreeNode node : MMOCore.plugin.skillTreeManager.getAllNodes()) { + for (SkillTreeNode node : MMOCore.plugin.skillTreeManager.getAllNodes()) { data.setNodeLevel(node, config.getInt("skill-tree-level." + node.getFullId(), 0)); } data.setupSkillTree(); @@ -127,12 +124,8 @@ public class YAMLPlayerDataManager extends PlayerDataManager { data.setStamina(config.contains("stamina") ? config.getDouble("stamina") : data.getStats().getStat("MAX_STAMINA")); data.setStellium(config.contains("stellium") ? config.getDouble("stellium") : data.getStats().getStat("MAX_STELLIUM")); - if (data.isOnline()) { - double health = config.contains("health") ? config.getDouble("health") : data.getStats().getStat("MAX_HEALTH"); - health = health == 0 ? 20 : health; - health = Math.min(health, data.getPlayer().getAttribute(Attribute.GENERIC_MAX_HEALTH).getValue()); - data.getPlayer().setHealth(health); - } + if (data.isOnline()) + data.getPlayer().setHealth(MMOCoreUtils.fixResource(config.getDouble("health"), data.getPlayer().getAttribute(Attribute.GENERIC_MAX_HEALTH).getValue())); data.setFullyLoaded(); } diff --git a/MMOCore-API/src/main/java/net/Indyuce/mmocore/player/DefaultPlayerData.java b/MMOCore-API/src/main/java/net/Indyuce/mmocore/player/DefaultPlayerData.java index a8fb4e65..b03f33e9 100644 --- a/MMOCore-API/src/main/java/net/Indyuce/mmocore/player/DefaultPlayerData.java +++ b/MMOCore-API/src/main/java/net/Indyuce/mmocore/player/DefaultPlayerData.java @@ -130,7 +130,7 @@ public class DefaultPlayerData implements ClassDataContainer { } @Override - public Map mapBoundSkills() { + public Map mapBoundSkills() { return new HashMap<>(); } diff --git a/MMOCore-API/src/main/java/net/Indyuce/mmocore/skill/RegisteredSkill.java b/MMOCore-API/src/main/java/net/Indyuce/mmocore/skill/RegisteredSkill.java index 46e9823f..cbbfa0ef 100644 --- a/MMOCore-API/src/main/java/net/Indyuce/mmocore/skill/RegisteredSkill.java +++ b/MMOCore-API/src/main/java/net/Indyuce/mmocore/skill/RegisteredSkill.java @@ -41,9 +41,9 @@ public class RegisteredSkill implements Unlockable { categories = config.getStringList("categories"); categories.add(getHandler().getId()); if (triggerType.isPassive()) - categories.add("passive"); + categories.add("PASSIVE"); else - categories.add("active"); + categories.add("ACTIVE"); // Load default modifier formulas for (String mod : handler.getModifiers()) @@ -139,7 +139,7 @@ public class RegisteredSkill implements Unlockable { String parsedExpression = formula; for (String category : categories) parsedExpression = parsedExpression.replace("<" + category + ">", "true"); - parsedExpression = parsedExpression.replaceAll("<.*>", "false"); + parsedExpression = parsedExpression.replaceAll("<.*?>", "false"); try { return (boolean) MythicLib.plugin.getInterpreter().eval(parsedExpression); } catch (EvalError error) { diff --git a/MMOCore-API/src/main/java/net/Indyuce/mmocore/api/player/profess/skillbinding/BoundSkillInfo.java b/MMOCore-API/src/main/java/net/Indyuce/mmocore/skill/binding/BoundSkillInfo.java similarity index 84% rename from MMOCore-API/src/main/java/net/Indyuce/mmocore/api/player/profess/skillbinding/BoundSkillInfo.java rename to MMOCore-API/src/main/java/net/Indyuce/mmocore/skill/binding/BoundSkillInfo.java index d2b948df..1d7920de 100644 --- a/MMOCore-API/src/main/java/net/Indyuce/mmocore/api/player/profess/skillbinding/BoundSkillInfo.java +++ b/MMOCore-API/src/main/java/net/Indyuce/mmocore/skill/binding/BoundSkillInfo.java @@ -1,4 +1,4 @@ -package net.Indyuce.mmocore.api.player.profess.skillbinding; +package net.Indyuce.mmocore.skill.binding; import io.lumine.mythic.lib.player.skill.PassiveSkill; import net.Indyuce.mmocore.api.player.PlayerData; @@ -27,12 +27,12 @@ public class BoundSkillInfo { * Used on update to refresh the classSkill & all references to old data. */ public BoundSkillInfo(BoundSkillInfo info) { - this.playerData=info.getPlayerData(); - this.classSkill= Objects.requireNonNull(playerData.getProfess().getSkill(info.getClassSkill().getSkill())); + this.playerData = info.getPlayerData(); + this.classSkill = Objects.requireNonNull(playerData.getProfess().getSkill(info.getClassSkill().getSkill())); info.unbind(); PassiveSkill passiveSkill = classSkill.toPassive(playerData); passiveSkill.register(playerData.getMMOPlayerData()); - this.passiveSkillUUID=passiveSkill.getUniqueId(); + this.passiveSkillUUID = passiveSkill.getUniqueId(); } public ClassSkill getClassSkill() { @@ -55,7 +55,7 @@ public class BoundSkillInfo { playerData.getMMOPlayerData().getPassiveSkillMap().removeModifier(passiveSkillUUID); PassiveSkill passiveSkill = classSkill.toPassive(playerData); passiveSkill.register(playerData.getMMOPlayerData()); - this.passiveSkillUUID=passiveSkill.getUniqueId(); + this.passiveSkillUUID = passiveSkill.getUniqueId(); } } diff --git a/MMOCore-API/src/main/java/net/Indyuce/mmocore/skill/binding/SkillSlot.java b/MMOCore-API/src/main/java/net/Indyuce/mmocore/skill/binding/SkillSlot.java new file mode 100644 index 00000000..9af1ccea --- /dev/null +++ b/MMOCore-API/src/main/java/net/Indyuce/mmocore/skill/binding/SkillSlot.java @@ -0,0 +1,116 @@ +package net.Indyuce.mmocore.skill.binding; + +import io.lumine.mythic.lib.api.MMOLineConfig; +import net.Indyuce.mmocore.MMOCore; +import net.Indyuce.mmocore.api.player.PlayerData; +import net.Indyuce.mmocore.player.Unlockable; +import net.Indyuce.mmocore.api.quest.trigger.SkillModifierTrigger; +import net.Indyuce.mmocore.skill.ClassSkill; +import org.bukkit.Material; +import org.bukkit.configuration.ConfigurationSection; + +import java.util.ArrayList; +import java.util.List; + +public class SkillSlot implements Unlockable { + private final int slot, modelData; + private final String formula; + private final String name; + private final List lore; + + private final boolean isUnlockedByDefault; + + private final boolean canManuallyBind; + + private final List skillBuffTriggers; + + private Material item; + + public SkillSlot(int slot, int modelData, String formula, String name, List lore, boolean isUnlockedByDefault, boolean canManuallyBind, List skillBuffTriggers) { + this.slot = slot; + this.modelData = modelData; + this.formula = formula; + this.name = name; + this.lore = lore; + this.canManuallyBind = canManuallyBind; + this.isUnlockedByDefault = isUnlockedByDefault; + this.skillBuffTriggers = skillBuffTriggers; + } + + public SkillSlot(ConfigurationSection section) { + this.slot = Integer.parseInt(section.getName()); + this.formula = section.contains("formula") ? section.getString("formula") : "true"; + this.name = section.getString("name"); + this.lore = section.getStringList("lore"); + if (section.contains("item")) + this.item = Material.valueOf(section.getString("item")); + this.modelData = section.getInt("model-data", 0); + isUnlockedByDefault = section.getBoolean("unlocked-by-default", true); + canManuallyBind = section.getBoolean("can-manually-bind", true); + skillBuffTriggers = new ArrayList<>(); + if (section.contains("skill-buffs")) + for (String skillBuff : section.getStringList("skill-buffs")) + if (skillBuff.startsWith("skill_buff")) + skillBuffTriggers.add((SkillModifierTrigger) MMOCore.plugin.loadManager.loadTrigger(new MMOLineConfig(skillBuff))); + } + + public int getSlot() { + return slot; + } + + public String getName() { + return name; + } + + public List getLore() { + return lore; + } + + public Material getItem() { + return item; + } + + public boolean hasItem() { + return item != null; + } + + public int getModelData() { + return modelData; + } + + public boolean isUnlockedByDefault() { + return isUnlockedByDefault; + } + + public List getSkillBuffTriggers() { + return skillBuffTriggers; + } + + public boolean canManuallyBind() { + return canManuallyBind; + } + + public boolean acceptsSkill(ClassSkill classSkill) { + return classSkill.getSkill().matchesFormula(formula); + } + + @Override + public String getUnlockNamespacedKey() { + return "slot:" + slot; + } + + /** + * If we lock a slot that had a skill bound + * to it we first unbind the attached skill. + */ + @Override + public void whenLocked(PlayerData playerData) { + if (playerData.hasSkillBound(slot)) + playerData.unbindSkill(slot); + } + + @Override + public void whenUnlocked(PlayerData playerData) { + + } +} diff --git a/MMOCore-Dist/src/main/resources/config.yml b/MMOCore-Dist/src/main/resources/config.yml index a796b374..cf27fcae 100644 --- a/MMOCore-Dist/src/main/resources/config.yml +++ b/MMOCore-Dist/src/main/resources/config.yml @@ -197,16 +197,14 @@ death-exp-loss: # Percentage of current EXP you lose when dying. percent: 30 -#Default max bound active and passive skills. -#These value can be modified for each class in the class yml. -max-bound-active-skills: 6 +#Maximum number of slot. This means that you can't unlock a slot greater than max-slots. (The slot count starts at 1 & end at max-slots). +max-slots: 8 #If you want players to bound their passive skills. #If false, all the passive skills unlocked will be active #Also set max-bound-passive-skills to 0 if seting passive-skill-need-bound to false. passive-skill-need-bound: true -max-bound-passive-skills: 3 # Fun extra RPG feature that switches the player's hotbar with # the 9 lower row items of his inventory. This allows the player diff --git a/MMOCore-Dist/src/main/resources/default/gui/skill-list.yml b/MMOCore-Dist/src/main/resources/default/gui/skill-list.yml index 0e88dd1a..444624e9 100644 --- a/MMOCore-Dist/src/main/resources/default/gui/skill-list.yml +++ b/MMOCore-Dist/src/main/resources/default/gui/skill-list.yml @@ -64,6 +64,7 @@ items: - '' - '&e► Left click to bind {selected}.' - '&e► Right click to unbind.' + - '&e► Shift left click to select.' skill-level: slots: [ 6,15,24,33,42,51 ] function: level diff --git a/MMOCore-Dist/src/main/resources/default/messages.yml b/MMOCore-Dist/src/main/resources/default/messages.yml index d091fa67..f4bcace5 100644 --- a/MMOCore-Dist/src/main/resources/default/messages.yml +++ b/MMOCore-Dist/src/main/resources/default/messages.yml @@ -203,6 +203,7 @@ upgrade-skill: '&eYour &6{skill} &eis now Level &6{level}&e!' not-unlocked-skill: '&cYou have not unlocked that skill yet.' no-skill-bound: '&cYou don''t have any skill bound to this slot.' not-compatible-skill: '&cThe selected skill is not compatible with this slot.' +cant-manually-bind: "&cYou can't manually bind/unbind a skill to this slot." 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.'