Merge remote-tracking branch 'origin/master'

# Conflicts:
#	MMOCore-API/src/main/java/net/Indyuce/mmocore/api/player/PlayerData.java
#	MMOCore-API/src/main/java/net/Indyuce/mmocore/api/player/profess/SavedClassInformation.java
#	README.md
This commit is contained in:
Jules 2023-04-08 14:28:40 +02:00
commit c8932e3a5d
39 changed files with 480 additions and 301 deletions

19
LICENSE
View File

@ -1,3 +1,18 @@
This project is owned and maintained by PhoenixDvpt and is distributed with "All Right's Reserved".
This project is owned and maintained by "Phoenix Development" and is distributed with "All Rights Reserved".
PhoenixDevt reserves the right to change these terms at any time, you have to comply with the most recent version.
We do NOT provide support to servers involved with piracy in any form, or owners who have downloaded ANY of our plugins from an unofficial/illegal source.
You can fork and modify the source code of this project as you wish to meet your server's needs, and merge requests are accepted under the terms that you grant full rights to us using any pushed code. However, you may not distribute the plugin for any purpose other than providing cross-compatibility with other Minecraft plugins, and may not bypass any anti-piracy protection.
Things can you CANNOT do:
- Issue a refund on PayPal without our explicit permission, as this is a digital good.
- Redistribute, sell or give an official/modified version of the plugin (with or without any type of counterpart) to anyone else.
- Modify and compile the project source code to bypass an anti-piracy protection.
- Download, compile, decompile or use the plugin on a production server without purchasing a license.
Things can you CAN do when purchasing the plugin:
- Download and decompile the plugin file.
- Fork and modify the project source code to meet your production server's needs.
- Use it on ONE production server or network (= proxy-connected servers) at a time.
You may propose a merge request, under the terms that you grant full rights to us using any pushed code.
If you are a developer and have not purchased a license, you have the permission to download, fork, edit and compile the project source code, and sell code modifications to your client ONLY IF they have already purchased a license. This only applies to one-time comission works and does NOT apply to public sales.

View File

@ -343,7 +343,6 @@ public class MMOCore extends JavaPlugin {
statManager.initialize(clearBefore);
professionManager.initialize(clearBefore);
InventoryManager.load();
skillTreeManager.initialize(clearBefore);
classManager.initialize(clearBefore);
questManager.initialize(clearBefore);
@ -353,6 +352,8 @@ public class MMOCore extends JavaPlugin {
requestManager.initialize(clearBefore);
soundManager.initialize(clearBefore);
configItems.initialize(clearBefore);
//Needs to be loaded after the class manager.
InventoryManager.load();
if (getConfig().isConfigurationSection("action-bar"))
actionBarManager.reload(getConfig().getConfigurationSection("action-bar"));

View File

@ -12,15 +12,12 @@ import net.Indyuce.mmocore.api.SoundEvent;
import net.Indyuce.mmocore.api.event.PlayerExperienceGainEvent;
import net.Indyuce.mmocore.api.event.PlayerLevelUpEvent;
import net.Indyuce.mmocore.api.event.PlayerResourceUpdateEvent;
import net.Indyuce.mmocore.api.event.unlocking.ItemLockedEvent;
import net.Indyuce.mmocore.api.event.unlocking.ItemUnlockedEvent;
import net.Indyuce.mmocore.api.player.attribute.PlayerAttribute;
import net.Indyuce.mmocore.api.player.attribute.PlayerAttributes;
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.api.player.social.FriendRequest;
import net.Indyuce.mmocore.api.player.stats.PlayerStats;
import net.Indyuce.mmocore.api.quest.PlayerQuests;
@ -31,12 +28,14 @@ import net.Indyuce.mmocore.experience.EXPSource;
import net.Indyuce.mmocore.experience.ExperienceObject;
import net.Indyuce.mmocore.experience.ExperienceTableClaimer;
import net.Indyuce.mmocore.experience.PlayerProfessions;
import net.Indyuce.mmocore.experience.droptable.ExperienceItem;
import net.Indyuce.mmocore.experience.droptable.ExperienceTable;
import net.Indyuce.mmocore.guild.provided.Guild;
import net.Indyuce.mmocore.loot.chest.particle.SmallParticleEffect;
import net.Indyuce.mmocore.party.AbstractParty;
import net.Indyuce.mmocore.party.provided.MMOCorePartyModule;
import net.Indyuce.mmocore.party.provided.Party;
import net.Indyuce.mmocore.player.ClassDataContainer;
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.cast.SkillCastingHandler;
@ -46,6 +45,7 @@ import net.Indyuce.mmocore.skilltree.SkillTreeNode;
import net.Indyuce.mmocore.skilltree.tree.SkillTree;
import net.Indyuce.mmocore.skilltree.tree.display.DisplayInfo;
import net.Indyuce.mmocore.skilltree.tree.display.Icon;
import net.Indyuce.mmocore.waypoint.Waypoint;
import net.Indyuce.mmocore.waypoint.WaypointOption;
import net.md_5.bungee.api.ChatMessageType;
import net.md_5.bungee.api.chat.TextComponent;
@ -79,10 +79,13 @@ public class PlayerData extends OfflinePlayerData implements Closable, Experienc
*/
@Nullable
private PlayerClass profess;
private int level, classPoints, skillPoints, attributePoints, attributeReallocationPoints;
private int skillTreeReallocationPoints, skillReallocationPoints;
private int level, classPoints, skillPoints, attributePoints, attributeReallocationPoints, skillTreeReallocationPoints, skillReallocationPoints;
private double experience;
private double mana, stamina, stellium;
/**
* Health is stored in playerData because when saving the playerData we can't access the player health anymore as the payer is Offline.
*/
private double health;
private Guild guild;
private SkillCastingHandler skillCasting;
private final PlayerQuests questData;
@ -263,7 +266,7 @@ public class PlayerData extends OfflinePlayerData implements Closable, Experienc
//Check the State of the node
if (nodeStatus != NodeStatus.UNLOCKED && nodeStatus != NodeStatus.UNLOCKABLE)
return false;
return getNodeLevel(node) < node.getMaxLevel() && (skillTreePoints.getOrDefault(node.getTree().getId(), 0) > 0 || skillTreePoints.getOrDefault("global", 0) > 0);
return getNodeLevel(node) < node.getMaxLevel() && (skillTreePoints.getOrDefault(node.getTree().getId(), 0) + skillTreePoints.getOrDefault("global", 0) >= node.getSkillTreePointsConsumed());
}
/**
@ -278,10 +281,14 @@ public class PlayerData extends OfflinePlayerData implements Closable, Experienc
if (nodeStates.get(node) == NodeStatus.UNLOCKABLE)
setNodeState(node, NodeStatus.UNLOCKED);
if (skillTreePoints.get(node.getTree().getId()) > 0)
withdrawSkillTreePoints(node.getTree().getId(), 1);
else
withdrawSkillTreePoints("global", 1);
int pointToWithdraw = node.getSkillTreePointsConsumed();
if (skillTreePoints.get(node.getTree().getId()) > 0) {
int pointWithdrawn = Math.min(pointToWithdraw, skillTreePoints.get(node.getTree().getId()));
withdrawSkillTreePoints(node.getTree().getId(), pointWithdrawn);
pointToWithdraw -= pointWithdrawn;
}
if (pointToWithdraw > 0)
withdrawSkillTreePoints("global", pointToWithdraw);
//We unload the nodeStates map (for the skill tree) and reload it completely
for (SkillTreeNode node1 : node.getTree().getNodes())
nodeStates.remove(node1);
@ -336,7 +343,7 @@ public class PlayerData extends OfflinePlayerData implements Closable, Experienc
}
public void setNodeLevel(SkillTreeNode node, int nodeLevel) {
int delta = nodeLevel - nodeLevels.getOrDefault(node, 0);
int delta = (nodeLevel - nodeLevels.getOrDefault(node, 0))*node.getSkillTreePointsConsumed();
pointSpent.put(node.getTree(), pointSpent.getOrDefault(node.getTree(), 0) + delta);
nodeLevels.put(node, nodeLevel);
}
@ -431,6 +438,8 @@ public class PlayerData extends OfflinePlayerData implements Closable, Experienc
@Override
public void close() {
health = getPlayer().getHealth();
// Remove from party if it is MMO Party Module
if (MMOCore.plugin.partyModule instanceof MMOCorePartyModule) {
AbstractParty party = getParty();
@ -989,7 +998,7 @@ public class PlayerData extends OfflinePlayerData implements Closable, Experienc
@Override
public double getHealth() {
return getPlayer().getHealth();
return isOnline() ? getPlayer().getHealth() : health;
}
@Override

View File

@ -316,7 +316,9 @@ public class SavedClassInformation {
player.setMana(mana);
player.setStellium(stellium);
player.setStamina(stamina);
double health=this.health;
health = health == 0 ? 20 : health;
player.getPlayer().setHealth(Math.min(health,player.getPlayer().getAttribute(Attribute.GENERIC_MAX_HEALTH).getValue()));
// Updates level on exp bar
player.refreshVanillaExp();
}

View File

@ -47,8 +47,6 @@ public class QuestProgress {
objective++;
objectiveProgress.close();
// apply triggers
objectiveProgress.getObjective().getTriggers().forEach(trigger -> trigger.schedule(getPlayer()));
// end quest
if (objective >= quest.getObjectives().size())
@ -57,6 +55,10 @@ public class QuestProgress {
objectiveProgress = nextObjective().newProgress(this);
player.getQuestData().updateBossBar();
// apply triggers at the end so the quest is ended when a trigger quest start is launched.
objectiveProgress.getObjective().getTriggers().forEach(trigger -> trigger.schedule(getPlayer()));
}
public String getFormattedLore() {

View File

@ -18,6 +18,9 @@ public class AttributesCommand extends RegisteredCommand {
@Override
public boolean execute(CommandSender sender, String label, String[] args) {
if (!sender.hasPermission("mmocore.attributes"))
return false;
if (!(sender instanceof Player)) {
sender.sendMessage(ChatColor.RED + "This command is for players only.");
return true;

View File

@ -25,6 +25,8 @@ public class FriendsCommand extends RegisteredCommand {
@Override
public boolean execute(CommandSender sender, String label, String[] args) {
if (!sender.hasPermission("mmocore.friends"))
return false;
if (!(sender instanceof Player)) {
sender.sendMessage(ChatColor.RED + "This command is for players only.");
return true;

View File

@ -26,6 +26,8 @@ public class GuildCommand extends RegisteredCommand {
@Override
public boolean execute(CommandSender sender, String label, String[] args) {
if (!sender.hasPermission("mmocore.guild"))
return false;
if (!(sender instanceof Player)) {
sender.sendMessage(ChatColor.RED + "This command is for players only.");
return true;

View File

@ -27,6 +27,8 @@ public class PartyCommand extends RegisteredCommand {
@Override
public boolean execute(CommandSender sender, String label, String[] args) {
if (!sender.hasPermission("mmocore.party"))
return false;
if (!(sender instanceof Player)) {
sender.sendMessage(ChatColor.RED + "This command is for players only.");
return true;

View File

@ -18,6 +18,8 @@ public class PlayerStatsCommand extends RegisteredCommand {
@Override
public boolean execute(CommandSender sender, String label, String[] args) {
if (!sender.hasPermission("mmocore.profile"))
return false;
if (!(sender instanceof Player)) {
sender.sendMessage(ChatColor.RED + "This command is for players only.");
return true;

View File

@ -17,6 +17,8 @@ public class QuestsCommand extends RegisteredCommand {
@Override
public boolean execute(CommandSender sender, String label, String[] args) {
if (!sender.hasPermission("mmocore.quests"))
return false;
if (sender instanceof Player) {
PlayerData data = PlayerData.get((Player) sender);
MMOCommandEvent event = new MMOCommandEvent(data, "quests");

View File

@ -19,6 +19,8 @@ public class SkillTreesCommand extends RegisteredCommand {
@Override
public boolean execute(@NotNull CommandSender sender, String s, String[] args) {
if (!sender.hasPermission("mmocore.skilltrees"))
return false;
if (!(sender instanceof Player player))
return false;
PlayerData data = PlayerData.get(player);

View File

@ -18,6 +18,8 @@ public class SkillsCommand extends RegisteredCommand {
@Override
public boolean execute(CommandSender sender, String label, String[] args) {
if (!sender.hasPermission("mmocore.skills"))
return false;
if (sender instanceof Player) {
PlayerData data = PlayerData.get((Player) sender);
MMOCommandEvent event = new MMOCommandEvent(data, "skills");

View File

@ -32,7 +32,7 @@ public enum ToggleableCommand {
PARTY("party", "Invite players in a party to split exp", config -> new PartyCommand(config)),
GUILD("guild", "Show players in current guild", config -> new GuildCommand(config)),
WITHDRAW("withdraw", "Withdraw money into coins and notes", config -> new WithdrawCommand(config), v -> MMOCore.plugin.hasEconomy() && MMOCore.plugin.economy.isValid(), "w"),
SKILL_TREES("skiltrees", "Open up the skill tree menu", config -> new SkillTreesCommand(config), "st", "trees", "tree"),
SKILL_TREES("skilltrees", "Open up the skill tree menu", config -> new SkillTreesCommand(config), "st", "trees", "tree"),
DEPOSIT("deposit", "Open the currency deposit menu", config -> new DepositCommand(config), "d"),
PVP_MODE("pvpmode", "Toggle on/off PVP mode.", config -> new PvpModeCommand(config), "pvp");

View File

@ -1,12 +1,12 @@
package net.Indyuce.mmocore.gui;
import io.lumine.mythic.lib.UtilityMethods;
import net.Indyuce.mmocore.MMOCore;
import net.Indyuce.mmocore.api.SoundEvent;
import net.Indyuce.mmocore.api.event.PlayerChangeClassEvent;
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.gui.api.EditableInventory;
import net.Indyuce.mmocore.gui.api.GeneratedInventory;
import net.Indyuce.mmocore.gui.api.InventoryClickContext;
@ -21,21 +21,14 @@ import org.bukkit.Material;
import org.bukkit.configuration.ConfigurationSection;
import org.bukkit.configuration.file.FileConfiguration;
import org.bukkit.inventory.ItemStack;
import org.jetbrains.annotations.Nullable;
import java.util.HashMap;
import java.util.Map;
public class ClassConfirmation extends EditableInventory {
/**
* This enables to configure the name of the
* class confirmation GUI (for custom GUI textures).
*/
private final Map<String, String> specificNames = new HashMap<>();
private final PlayerClass playerClass;
public ClassConfirmation() {
super("class-confirm");
public ClassConfirmation(PlayerClass playerClass, boolean isDefault) {
super("class-confirm-" + (isDefault ? "default" : UtilityMethods.ymlName(playerClass.getId())));
this.playerClass = playerClass;
}
@Override
@ -43,21 +36,13 @@ public class ClassConfirmation extends EditableInventory {
return function.equalsIgnoreCase("yes") ? new YesItem(config) : new SimplePlaceholderItem(config);
}
public GeneratedInventory newInventory(PlayerData data, PlayerClass profess, PluginInventory last) {
return new ClassConfirmationInventory(data, this, profess, last);
public GeneratedInventory newInventory(PlayerData data, PluginInventory last) {
return new ClassConfirmationInventory(data, this, playerClass, last);
}
@Override
public void reload(FileConfiguration config) {
super.reload(config);
specificNames.clear();
if (config.contains("class-specific-name")) {
final ConfigurationSection section = config.getConfigurationSection("class-specific-name");
for (String key : section.getKeys(false))
specificNames.put(key, section.getString(key));
}
}
public class UnlockedItem extends InventoryItem<ClassConfirmationInventory> {
@ -154,9 +139,7 @@ public class ClassConfirmation extends EditableInventory {
@Override
public String calculateName() {
final String professKey = MMOCoreUtils.ymlName(profess.getId());
final @Nullable String found = specificNames.get(professKey);
return found == null ? getName().replace("{class}", profess.getName()) : found;
return getName().replace("{class}", profess.getName());
}
}
}

View File

@ -1,18 +1,21 @@
package net.Indyuce.mmocore.gui;
import io.lumine.mythic.lib.MythicLib;
import io.lumine.mythic.lib.UtilityMethods;
import net.Indyuce.mmocore.MMOCore;
import net.Indyuce.mmocore.api.ConfigMessage;
import net.Indyuce.mmocore.api.SoundEvent;
import net.Indyuce.mmocore.api.player.PlayerData;
import net.Indyuce.mmocore.api.player.profess.ClassOption;
import net.Indyuce.mmocore.api.player.profess.PlayerClass;
import net.Indyuce.mmocore.api.util.MMOCoreUtils;
import net.Indyuce.mmocore.gui.api.EditableInventory;
import net.Indyuce.mmocore.gui.api.GeneratedInventory;
import net.Indyuce.mmocore.gui.api.InventoryClickContext;
import net.Indyuce.mmocore.gui.api.item.InventoryItem;
import net.Indyuce.mmocore.gui.api.item.SimplePlaceholderItem;
import net.Indyuce.mmocore.manager.InventoryManager;
import org.apache.commons.lang.Validate;
import org.bukkit.Material;
import org.bukkit.NamespacedKey;
import org.bukkit.configuration.ConfigurationSection;
@ -24,6 +27,7 @@ import org.bukkit.persistence.PersistentDataType;
import java.util.ArrayList;
import java.util.Comparator;
import java.util.List;
import java.util.Objects;
import java.util.stream.Collectors;
public class ClassSelect extends EditableInventory {
@ -33,7 +37,7 @@ public class ClassSelect extends EditableInventory {
@Override
public InventoryItem load(String function, ConfigurationSection config) {
return function.equals("class") ? new ClassItem(config) : new SimplePlaceholderItem(config);
return function.startsWith("class") ? new ClassItem(config) : new SimplePlaceholderItem(config);
}
public GeneratedInventory newInventory(PlayerData data) {
@ -43,10 +47,13 @@ public class ClassSelect extends EditableInventory {
public class ClassItem extends SimplePlaceholderItem<ProfessSelectionInventory> {
private final String name;
private final List<String> lore;
private final PlayerClass playerClass;
public ClassItem(ConfigurationSection config) {
super(Material.BARRIER, config);
Validate.isTrue(config.getString("function").length()>6,"Couldn't find the class associated to: "+config.getString("function"));
String classId = UtilityMethods.enumName(config.getString("function").substring(6));
this.playerClass = Objects.requireNonNull(MMOCore.plugin.classManager.get(classId),classId+" does not correspond to any classId.");
this.name = config.getString("name");
this.lore = config.getStringList("lore");
}
@ -57,36 +64,34 @@ public class ClassSelect extends EditableInventory {
@Override
public ItemStack display(ProfessSelectionInventory inv, int n) {
if (n >= inv.classes.size())
return null;
PlayerClass profess = inv.classes.get(n);
ItemStack item = profess.getIcon();
ItemStack item = playerClass.getIcon();
ItemMeta meta = item.getItemMeta();
if (hideFlags())
meta.addItemFlags(ItemFlag.values());
meta.setDisplayName(MythicLib.plugin.parseColors(name).replace("{name}", profess.getName()));
meta.setDisplayName(MythicLib.plugin.parseColors(name).replace("{name}", playerClass.getName()));
List<String> lore = new ArrayList<>(this.lore);
int index = lore.indexOf("{lore}");
if (index >= 0) {
lore.remove(index);
for (int j = 0; j < profess.getDescription().size(); j++)
lore.add(index + j, profess.getDescription().get(j));
for (int j = 0; j < playerClass.getDescription().size(); j++)
lore.add(index + j, playerClass.getDescription().get(j));
}
index = lore.indexOf("{attribute-lore}");
if (index >= 0) {
lore.remove(index);
for (int j = 0; j < profess.getAttributeDescription().size(); j++)
lore.add(index + j, profess.getAttributeDescription().get(j));
for (int j = 0; j < playerClass.getAttributeDescription().size(); j++)
lore.add(index + j, playerClass.getAttributeDescription().get(j));
}
meta.getPersistentDataContainer().set(new NamespacedKey(MMOCore.plugin, "class_id"), PersistentDataType.STRING, profess.getId());
meta.getPersistentDataContainer().set(new NamespacedKey(MMOCore.plugin, "class_id"), PersistentDataType.STRING, playerClass.getId());
meta.setLore(lore);
item.setItemMeta(meta);
return item;
}
}
public class ProfessSelectionInventory extends GeneratedInventory {
@ -104,10 +109,8 @@ public class ClassSelect extends EditableInventory {
@Override
public void whenClicked(InventoryClickContext context, InventoryItem item) {
if (item.getFunction().equals("class")) {
String classId = context.getClickedItem().getItemMeta().getPersistentDataContainer().get(new NamespacedKey(MMOCore.plugin, "class_id"), PersistentDataType.STRING);
if (classId.equals(""))
return;
if (item instanceof ClassItem) {
PlayerClass profess = ((ClassItem) item).playerClass;
if (playerData.getClassPoints() < 1) {
MMOCore.plugin.soundManager.getSound(SoundEvent.CANT_SELECT_CLASS).playTo(player);
@ -115,7 +118,6 @@ public class ClassSelect extends EditableInventory {
return;
}
final PlayerClass profess = MMOCore.plugin.classManager.get(classId);
if (profess.hasOption(ClassOption.NEEDS_PERMISSION) && !player.hasPermission("mmocore.class." + profess.getId().toLowerCase())) {
MMOCore.plugin.soundManager.getSound(SoundEvent.CANT_SELECT_CLASS).playTo(player);
new ConfigMessage("no-permission-for-class").send(player);
@ -129,7 +131,7 @@ public class ClassSelect extends EditableInventory {
}
final PlayerClass playerClass = findDeepestSubclass(playerData, profess);
InventoryManager.CLASS_CONFIRM.newInventory(playerData, playerClass, this).open();
InventoryManager.CLASS_CONFIRM.get(MMOCoreUtils.ymlName(playerClass.getId())).newInventory(playerData, this).open();
}
}
}

View File

@ -248,6 +248,7 @@ public class SkillTreeViewer extends EditableInventory {
holders.register("max-level", node.getMaxLevel());
holders.register("max-children", node.getMaxChildren());
holders.register("size", node.getSize());
holders.register("point-consumed", node.getSkillTreePointsConsumed());
}
int maxPointSpent = inv.getSkillTree().getMaxPointSpent();
holders.register("max-point-spent", maxPointSpent == Integer.MAX_VALUE ? "" : maxPointSpent);
@ -367,7 +368,7 @@ public class SkillTreeViewer extends EditableInventory {
open();
}
if (item.getFunction().equals("reallocation")) {
int spent = playerData.countSkillTreePoints(skillTree);
int spent = playerData.getPointSpent(skillTree);
if (spent < 1) {
MMOCore.plugin.configManager.getSimpleMessage("no-skill-tree-points-spent").send(player);
MMOCore.plugin.soundManager.getSound(SoundEvent.NOT_ENOUGH_POINTS).playTo(getPlayer());
@ -381,7 +382,7 @@ public class SkillTreeViewer extends EditableInventory {
event.setCancelled(true);
return;
} else {
int reallocated = playerData.countSkillTreePoints(skillTree);
int reallocated = playerData.getPointSpent(skillTree);
//We remove all the nodeStates progress
playerData.giveSkillTreePoints(skillTree.getId(), reallocated);
playerData.giveSkillTreeReallocationPoints(-1);
@ -446,7 +447,7 @@ public class SkillTreeViewer extends EditableInventory {
//Else the player doesn't doesn't have the skill tree points
else {
MMOCore.plugin.configManager.getSimpleMessage("not-enough-skill-tree-points").send(player);
MMOCore.plugin.configManager.getSimpleMessage("not-enough-skill-tree-points", "point", "" + node.getSkillTreePointsConsumed()).send(player);
MMOCore.plugin.soundManager.getSound(SoundEvent.NOT_ENOUGH_POINTS).playTo(getPlayer());
event.setCancelled(true);
return;

View File

@ -1,77 +0,0 @@
package net.Indyuce.mmocore.gui;
import net.Indyuce.mmocore.MMOCore;
import net.Indyuce.mmocore.api.player.PlayerData;
import net.Indyuce.mmocore.gui.api.EditableInventory;
import net.Indyuce.mmocore.gui.api.GeneratedInventory;
import net.Indyuce.mmocore.gui.api.InventoryClickContext;
import net.Indyuce.mmocore.gui.api.PluginInventory;
import net.Indyuce.mmocore.gui.api.item.InventoryItem;
import net.Indyuce.mmocore.gui.api.item.Placeholders;
import net.Indyuce.mmocore.gui.api.item.SimplePlaceholderItem;
import net.Indyuce.mmocore.api.event.PlayerChangeClassEvent;
import net.Indyuce.mmocore.api.player.profess.PlayerClass;
import net.Indyuce.mmocore.api.SoundEvent;
import org.bukkit.Bukkit;
import org.bukkit.configuration.ConfigurationSection;
public class SubclassConfirmation extends EditableInventory {
public SubclassConfirmation() {
super("subclass-confirm");
}
@Override
public InventoryItem load(String function, ConfigurationSection config) {
return function.equalsIgnoreCase("yes") ? new InventoryItem<SubclassConfirmationInventory>(config) {
@Override
public Placeholders getPlaceholders(SubclassConfirmationInventory inv, int n) {
Placeholders holders = new Placeholders();
holders.register("class", inv.profess.getName());
return holders;
}
} : new SimplePlaceholderItem(config);
}
public GeneratedInventory newInventory(PlayerData data, PlayerClass profess, PluginInventory last) {
return new SubclassConfirmationInventory(data, this, profess, last);
}
public class SubclassConfirmationInventory extends GeneratedInventory {
private final PlayerClass profess;
private final PluginInventory last;
public SubclassConfirmationInventory(PlayerData playerData, EditableInventory editable, PlayerClass profess, PluginInventory last) {
super(playerData, editable);
this.profess = profess;
this.last = last;
}
@Override
public void whenClicked(InventoryClickContext context, InventoryItem item) {
if (item.getFunction().equals("back"))
last.open();
else if (item.getFunction().equals("yes")) {
PlayerChangeClassEvent called = new PlayerChangeClassEvent(playerData, profess);
Bukkit.getPluginManager().callEvent(called);
if (called.isCancelled())
return;
playerData.setClass(profess);
MMOCore.plugin.configManager.getSimpleMessage("class-select", "class", profess.getName()).send(player);
MMOCore.plugin.soundManager.getSound(SoundEvent.SELECT_CLASS).playTo(player);
player.closeInventory();
}
}
@Override
public String calculateName() {
return getName();
}
}
}

View File

@ -1,8 +1,7 @@
package net.Indyuce.mmocore.gui;
import io.lumine.mythic.lib.MythicLib;
import io.lumine.mythic.lib.api.item.ItemTag;
import io.lumine.mythic.lib.api.item.NBTItem;
import io.lumine.mythic.lib.UtilityMethods;
import net.Indyuce.mmocore.MMOCore;
import net.Indyuce.mmocore.api.player.PlayerData;
import net.Indyuce.mmocore.gui.api.EditableInventory;
@ -15,116 +14,118 @@ import net.Indyuce.mmocore.api.player.profess.PlayerClass;
import net.Indyuce.mmocore.api.ConfigMessage;
import net.Indyuce.mmocore.api.SoundEvent;
import net.Indyuce.mmocore.api.player.profess.Subclass;
import org.apache.commons.lang.Validate;
import org.bukkit.Material;
import org.bukkit.NamespacedKey;
import org.bukkit.configuration.ConfigurationSection;
import org.bukkit.inventory.ItemFlag;
import org.bukkit.inventory.ItemStack;
import org.bukkit.inventory.meta.ItemMeta;
import org.bukkit.persistence.PersistentDataType;
import java.util.ArrayList;
import java.util.List;
import java.util.Objects;
import java.util.stream.Collectors;
public class SubclassSelect extends EditableInventory {
public SubclassSelect() {
super("subclass-select");
}
public SubclassSelect() {
super("subclass-select");
}
@Override
public InventoryItem load(String function, ConfigurationSection config) {
return function.equals("class") ? new ClassItem(config) : new SimplePlaceholderItem(config);
}
@Override
public InventoryItem load(String function, ConfigurationSection config) {
return function.startsWith("sub-class") ? new ClassItem(config) : new SimplePlaceholderItem(config);
}
public GeneratedInventory newInventory(PlayerData data) {
return new SubclassSelectionInventory(data, this);
}
public GeneratedInventory newInventory(PlayerData data) {
return new SubclassSelectionInventory(data, this);
}
public class ClassItem extends SimplePlaceholderItem<SubclassSelectionInventory> {
private final String name;
private final List<String> lore;
public class ClassItem extends SimplePlaceholderItem<SubclassSelectionInventory> {
private final String name;
private final List<String> lore;
private final PlayerClass playerClass;
public ClassItem(ConfigurationSection config) {
super(Material.BARRIER, config);
public ClassItem(ConfigurationSection config) {
super(Material.BARRIER, config);
Validate.isTrue(config.getString("function").length() > 10, "Couldn't find the class associated to: " + config.getString("function"));
String classId = UtilityMethods.enumName(config.getString("function").substring(10));
this.playerClass = Objects.requireNonNull(MMOCore.plugin.classManager.get(classId), classId + " does not correspond to any classId.");
this.name = config.getString("name");
this.lore = config.getStringList("lore");
}
this.name = config.getString("name");
this.lore = config.getStringList("lore");
}
public boolean hasDifferentDisplay() {
return true;
}
@Override
public boolean hasDifferentDisplay() {
return true;
}
@Override
public ItemStack display(SubclassSelectionInventory inv, int n) {
ItemStack item = playerClass.getIcon();
ItemMeta meta = item.getItemMeta();
if (hideFlags())
meta.addItemFlags(ItemFlag.values());
meta.setDisplayName(MythicLib.plugin.parseColors(name).replace("{name}", playerClass.getName()));
List<String> lore = new ArrayList<>(this.lore);
@Override
public ItemStack display(SubclassSelectionInventory inv, int n) {
if (n >= inv.subclasses.size())
return null;
int index = lore.indexOf("{lore}");
if (index >= 0) {
lore.remove(index);
for (int j = 0; j < playerClass.getDescription().size(); j++)
lore.add(index + j, playerClass.getDescription().get(j));
}
PlayerClass profess = inv.subclasses.get(n).getProfess();
index = lore.indexOf("{attribute-lore}");
if (index >= 0) {
lore.remove(index);
for (int j = 0; j < playerClass.getAttributeDescription().size(); j++)
lore.add(index + j, playerClass.getAttributeDescription().get(j));
}
ItemStack item = profess.getIcon();
ItemMeta meta = item.getItemMeta();
meta.setDisplayName(MythicLib.plugin.parseColors(name).replace("{name}", profess.getName()));
List<String> lore = new ArrayList<>(this.lore);
meta.getPersistentDataContainer().set(new NamespacedKey(MMOCore.plugin, "class_id"), PersistentDataType.STRING, playerClass.getId());
meta.setLore(lore);
item.setItemMeta(meta);
return item;
}
int index = lore.indexOf("{lore}");
if (index >= 0) {
lore.remove(index);
for (int j = 0; j < profess.getDescription().size(); j++)
lore.add(index + j, profess.getDescription().get(j));
}
@Override
public boolean canDisplay(SubclassSelectionInventory inv) {
return inv.getPlayerData().getProfess().hasSubclass(playerClass);
}
}
index = lore.indexOf("{attribute-lore}");
if (index >= 0) {
lore.remove(index);
for (int j = 0; j < profess.getAttributeDescription().size(); j++)
lore.add(index + j, profess.getAttributeDescription().get(j));
}
public class SubclassSelectionInventory extends GeneratedInventory {
private final List<Subclass> subclasses;
meta.setLore(lore);
item.setItemMeta(meta);
return NBTItem.get(item).addTag(new ItemTag("classId", profess.getId())).toItem();
}
public SubclassSelectionInventory(PlayerData playerData, EditableInventory editable) {
super(playerData, editable);
@Override
public boolean canDisplay(SubclassSelectionInventory inv) {
return true;
}
}
subclasses = playerData.getProfess().getSubclasses().stream().filter(sub -> playerData.getLevel() >= sub.getLevel())
.collect(Collectors.toList());
}
public class SubclassSelectionInventory extends GeneratedInventory {
private final List<Subclass> subclasses;
@Override
public String calculateName() {
return getName();
}
public SubclassSelectionInventory(PlayerData playerData, EditableInventory editable) {
super(playerData, editable);
@Override
public void whenClicked(InventoryClickContext context, InventoryItem item) {
if (item.getFunction().equals("back"))
InventoryManager.CLASS_SELECT.newInventory(playerData).open();
subclasses = playerData.getProfess().getSubclasses().stream().filter(sub -> playerData.getLevel() >= sub.getLevel())
.collect(Collectors.toList());
}
if (item.getFunction().startsWith("sub-class")) {
String classId= item.getFunction().substring(10);
@Override
public String calculateName() {
return getName();
}
@Override
public void whenClicked(InventoryClickContext context, InventoryItem item) {
if (item.getFunction().equals("back"))
InventoryManager.CLASS_SELECT.newInventory(playerData).open();
if (item.getFunction().equals("class")) {
String tag = NBTItem.get(context.getClickedItem()).getString("classId");
if (tag.equals(""))
return;
if (playerData.getClassPoints() < 1) {
player.closeInventory();
MMOCore.plugin.soundManager.getSound(SoundEvent.CANT_SELECT_CLASS).playTo(getPlayer());
new ConfigMessage("cant-choose-new-class").send(player);
return;
}
InventoryManager.SUBCLASS_CONFIRM.newInventory(playerData, MMOCore.plugin.classManager.get(tag), this).open();
}
}
}
if (playerData.getClassPoints() < 1) {
player.closeInventory();
MMOCore.plugin.soundManager.getSound(SoundEvent.CANT_SELECT_CLASS).playTo(getPlayer());
new ConfigMessage("cant-choose-new-class").send(player);
return;
}
InventoryManager.CLASS_CONFIRM.get(classId).newInventory(playerData, this).open();
}
}
}
}

View File

@ -182,12 +182,19 @@ public class ConfigManager {
}
public void loadDefaultFile(String path, String name) {
String newPath = path.isEmpty() ? "" : "/" + path;
File folder = new File(MMOCore.plugin.getDataFolder() + (newPath));
if (!folder.exists()) folder.mkdir();
String newPath ="";
if(!path.isEmpty()){
String[] subpaths = path.split("/");
for (String subpath : subpaths) {
newPath+="/"+subpath;
File folder = new File(MMOCore.plugin.getDataFolder() + (newPath));
if (!folder.exists()) folder.mkdir();
}
}
File file = new File(MMOCore.plugin.getDataFolder() + (newPath), name);
if (!file.exists()) try {
MMOCore.log("default/" + (path.isEmpty() ? "" : path + "/") + name);
Files.copy(MMOCore.plugin.getResource("default/" + (path.isEmpty() ? "" : path + "/") + name), file.getAbsoluteFile().toPath());
} catch (IOException e) {
e.printStackTrace();

View File

@ -1,10 +1,11 @@
package net.Indyuce.mmocore.manager;
import java.util.Arrays;
import java.util.List;
import java.util.*;
import java.util.logging.Level;
import net.Indyuce.mmocore.MMOCore;
import net.Indyuce.mmocore.api.player.profess.PlayerClass;
import net.Indyuce.mmocore.api.util.MMOCoreUtils;
import net.Indyuce.mmocore.gui.social.friend.EditableFriendList;
import net.Indyuce.mmocore.gui.*;
import net.Indyuce.mmocore.gui.social.friend.EditableFriendRemoval;
@ -20,8 +21,7 @@ public class InventoryManager {
public static final SkillList SKILL_LIST = new SkillList();
public static final ClassSelect CLASS_SELECT = new ClassSelect();
public static final SubclassSelect SUBCLASS_SELECT = new SubclassSelect();
public static final ClassConfirmation CLASS_CONFIRM = new ClassConfirmation();
public static final SubclassConfirmation SUBCLASS_CONFIRM = new SubclassConfirmation();
public static final Map<String, ClassConfirmation> CLASS_CONFIRM = new HashMap<>();
public static final WaypointViewer WAYPOINTS = new WaypointViewer();
public static final EditableFriendList FRIEND_LIST = new EditableFriendList();
public static final EditableFriendRemoval FRIEND_REMOVAL = new EditableFriendRemoval();
@ -32,15 +32,39 @@ public class InventoryManager {
public static final QuestViewer QUEST_LIST = new QuestViewer();
public static final AttributeView ATTRIBUTE_VIEW = new AttributeView();
public static final SkillTreeViewer TREE_VIEW = new SkillTreeViewer();
public static final List<EditableInventory> list = Arrays.asList(PLAYER_STATS, ATTRIBUTE_VIEW, TREE_VIEW,SKILL_LIST, CLASS_SELECT, SUBCLASS_SELECT, SUBCLASS_CONFIRM, QUEST_LIST, WAYPOINTS, CLASS_CONFIRM, FRIEND_LIST, FRIEND_REMOVAL, PARTY_VIEW, PARTY_CREATION, GUILD_VIEW, GUILD_CREATION);
public static final List<EditableInventory> list = new ArrayList(Arrays.asList(PLAYER_STATS, ATTRIBUTE_VIEW, TREE_VIEW, SKILL_LIST, CLASS_SELECT, SUBCLASS_SELECT, QUEST_LIST, WAYPOINTS, FRIEND_LIST, FRIEND_REMOVAL, PARTY_VIEW, PARTY_CREATION, GUILD_VIEW, GUILD_CREATION));
private static List<String> defaultClass = Arrays.asList(new String[]{"human", "mage", "paladin", "warrior", "rogue", "arcane-mage"});
public static void load() {
list.forEach(inv -> {
MMOCore.plugin.configManager.loadDefaultFile("gui", inv.getId() + ".yml");
String classConfirmFolder = "gui/class-confirm";
try {
MMOCore.plugin.configManager.loadDefaultFile(classConfirmFolder, "class-confirm-default.yml");
} catch (Exception exception) {
MMOCore.log(Level.WARNING, "Could not load inventory 'class-confirm/class-confirm-default" + "': " + exception.getMessage());
}
for (PlayerClass playerClass : MMOCore.plugin.classManager.getAll()) {
String classId = MMOCoreUtils.ymlName(playerClass.getId());
ConfigFile configFile = new ConfigFile(classConfirmFolder, "class-confirm-" + classId);
ClassConfirmation GUI;
if (configFile.exists())
GUI = new ClassConfirmation(playerClass, false);
else {
GUI = new ClassConfirmation(playerClass, true);
}
CLASS_CONFIRM.put(MMOCoreUtils.ymlName(playerClass.getId()), GUI);
GUI.reload(new ConfigFile("/" +classConfirmFolder, GUI.getId()).getConfig());
}
list.forEach(inv ->
{
String folder = "gui" + (inv instanceof ClassConfirmation ? "/class-confirm" : "");
try {
inv.reload(new ConfigFile("/gui", inv.getId()).getConfig());
} catch (IllegalArgumentException exception) {
MMOCore.log(Level.WARNING, "Could not load inventory '" + inv.getId() + "': " + exception.getMessage());
MMOCore.plugin.configManager.loadDefaultFile(folder, inv.getId() + ".yml");
inv.reload(new ConfigFile("/" + folder, inv.getId()).getConfig());
} catch (Exception exception) {
MMOCore.log(Level.WARNING, "Could not load inventory '" + (inv instanceof ClassConfirmation ? "class-confirm/" : "") + inv.getId() + "': " + exception.getMessage());
}
});
}

View File

@ -45,13 +45,13 @@ public abstract class PlayerDataManager {
*/
public void unregisterSafe(PlayerData playerData) {
// Save data async if required
if (playerData.isFullyLoaded())
Bukkit.getScheduler().runTaskAsynchronously(MMOCore.plugin, () -> saveData(playerData, true));
// Close and unregister data instantly if no error occured
playerData.close();
data.remove(playerData.getUniqueId());
// Save data async if required
if (playerData.isFullyLoaded())
Bukkit.getScheduler().runTaskAsynchronously(MMOCore.plugin, () -> saveData(playerData, true));
}
/**

View File

@ -113,15 +113,21 @@ public class MMOCoreDataSynchronizer extends DataSynchronizer {
}
}
//These should be loaded after to make sure that the MAX_MANA, MAX_STAMINA & MAX_STELLIUM stats are already loaded.
/*
* These should be loaded after to make sure that the
* MAX_MANA, MAX_STAMINA & MAX_STELLIUM stats are already loaded.
*/
data.setHealth(result.getDouble("health"));
data.setMana(result.getDouble("mana"));
data.setStamina(result.getDouble("stamina"));
data.setStellium(result.getDouble("stellium"));
double health = result.getDouble("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()) {
double health = data.getHealth();
health = health == 0 ? data.getPlayer().getAttribute(Attribute.GENERIC_MAX_HEALTH).getValue() : health;
health = Math.max(Math.min(health, data.getPlayer().getAttribute(Attribute.GENERIC_MAX_HEALTH).getValue()), 0);
data.getPlayer().setHealth(health);
}
UtilityMethods.debug(MMOCore.plugin, "SQL", String.format("{ class: %s, level: %d }", data.getProfess().getId(), data.getLevel()));
data.setFullyLoaded();

View File

@ -118,14 +118,22 @@ public class YAMLPlayerDataManager extends PlayerDataManager {
MMOCore.log(Level.WARNING, "Could not load class info '" + key + "': " + exception.getMessage());
}
//These should be loaded after to make sure that the MAX_MANA, MAX_STAMINA & MAX_STELLIUM stats are already loaded.
/*
* These should be loaded after to make sure that the
* MAX_MANA, MAX_STAMINA & MAX_STELLIUM stats are already loaded.
*/
data.setHealth(config.getDouble("health"));
data.setMana(config.contains("mana") ? config.getDouble("mana") : data.getStats().getStat("MAX_MANA"));
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"));
double health=config.contains("health") ? config.getDouble("health") : data.getStats().getStat("MAX_HEALTH");
health=Math.min(health,data.getPlayer().getAttribute(Attribute.GENERIC_MAX_HEALTH).getValue());
data.getPlayer().setHealth(health);
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);
}
data.setFullyLoaded();
}

View File

@ -22,6 +22,10 @@ public class SkillTreeNode implements ExperienceObject {
private final SkillTree tree;
private final String name, id;
private IntegerCoordinates coordinates;
/**
* The number of skill tree points this node requires.
*/
private final int skillTreePointsConsumed;
private boolean isRoot;
/**
@ -56,7 +60,8 @@ public class SkillTreeNode implements ExperienceObject {
name = Objects.requireNonNull(config.getString("name"), "Could not find node name");
size = Objects.requireNonNull(config.getInt("size"));
isRoot = config.getBoolean("is-root", false);
skillTreePointsConsumed=config.getInt("point-consumed",1);
Validate.isTrue(skillTreePointsConsumed>0,"The skill tree points consumed by a node must be greater than 0.");
if (config.contains("lores"))
for (String key : config.getConfigurationSection("lores").getKeys(false))
try {
@ -100,6 +105,10 @@ public class SkillTreeNode implements ExperienceObject {
children.add(child);
}
public int getSkillTreePointsConsumed() {
return skillTreePointsConsumed;
}
public void setCoordinates(IntegerCoordinates coordinates) {
this.coordinates = coordinates;
}
@ -112,6 +121,7 @@ public class SkillTreeNode implements ExperienceObject {
return softParents.containsKey(parent) || strongParents.containsKey(parent);
}
public int getMaxLevel() {
return maxLevel;
}

View File

@ -1,16 +1,6 @@
# GUI display name, used by default
name: 'Confirmation: {class}'
# GUI display name which overrides the default one.
# This can be used to apply custom textures to the GUI
class-specific-name:
mage: "Select Mage"
rogue: "Select Rogue"
marksman: "Select Marksman"
warrior: "Select Warrior"
paladin: "Select Paladin"
# Number of slots in your inventory. Must be
# between 9 and 54 and must be a multiple of 9.
slots: 27

View File

@ -7,10 +7,51 @@ name: Class Selection
slots: 27
items:
class:
slots: [13,12,14,11,15,10,16]
function: class
name: '&a&lThe {name}'
class-rogue:
slots: [11]
function: class-rogue
name: '&a&lThe Rogue'
hide-flags: true
lore:
- '{lore}'
- ''
- '{attribute-lore}'
class-mage:
slots: [12]
function: class-mage
name: '&a&lThe Mage'
hide-flags: true
lore:
- '{lore}'
- ''
- '{attribute-lore}'
class-marksman:
slots: [13]
function: class-marksman
name: '&a&lThe Marksman'
hide-flags: true
lore:
- '{lore}'
- ''
- '{attribute-lore}'
class-warrior:
slots: [14]
function: class-warrior
name: '&a&lThe Warrior'
hide-flags: true
lore:
- '{lore}'
- ''
- '{attribute-lore}'
class-paladin:
slots: [15]
function: class-paladin
name: '&a&lThe Paladin'
hide-flags: true
lore:
- '{lore}'

View File

@ -83,6 +83,7 @@ items:
- '&7Current Level: &6{current-level}'
- '&7Max Level: &6{max-level}'
- '&7Max Children: &6{max-children}'
- '&7Points required: &6{point-consumed}'
- '&7Size: &6{size}'
- '--------------------'
- '&7⧆ &6Requirements: '

View File

@ -1,21 +0,0 @@
# GUI display name
name: Subclass Confirmation
# Number of slots in your inventory. Must be
# between 9 and 54 and must be a multiple of 9.
slots: 27
items:
yes:
slots: [12]
item: GREEN_TERRACOTTA
function: 'yes'
name: '&aSelect {class}'
lore: {}
back:
slots: [14]
item: RED_TERRACOTTA
function: back
name: '&aBack'
lore: {}

View File

@ -13,10 +13,10 @@ items:
item: RED_STAINED_GLASS_PANE
name: '&aBack to Class Selection'
lore: []
class:
slots: [13,12,14,11,15,10,16]
function: class
name: '&a&lThe {name}'
arcane-mage:
slots: [13]
function: sub-class-arcane-mage
name: '&a&lThe Arcane Mage'
lore:
- '{lore}'
- ''

View File

@ -215,7 +215,7 @@ no-skill-tree-points-spent: '&cYou have not spent any skill tree points.'
locked-node: '&cThis skill is locked!'
upgrade-skill-node: '&eYour skill node &6{skill-node} &eis now Level &6{level}&e!'
skill-node-max-level-hit: '&cYou already hit the max level for that skill node.'
not-enough-skill-tree-points: '&cYou need one skill tree point.'
not-enough-skill-tree-points: '&cYou need {point} skill tree point.'
reallocated-points: '&eYou successfully reset the skill tree {skill-tree}. &eYou now have &6{points} &eskill tree points.'
not-skill-tree-reallocation-point: '&cYou do not have 1 skill tree reallocation point.'
no-skill-tree: '&cYour class doesn''t have any skill tree.'

View File

@ -15,6 +15,7 @@ nodes:
max-level: 1
is-root: true
size: 1
point-consumed: 1
experience-table: skilltree_cooldown_reduction5
lores:
0:
@ -27,6 +28,7 @@ nodes:
a2:
name: 'Cooldown Reduction'
size: 1
point-consumed: 1
max-children: 1
experience-table: skilltree_cooldown_reduction10
coordinates:
@ -41,6 +43,7 @@ nodes:
a3:
name: 'Cooldown Reduction'
size: 1
point-consumed: 1
max-children: 1
experience-table: skilltree_cooldown_reduction15
coordinates:
@ -57,6 +60,7 @@ nodes:
b1:
name: 'Critical Strike Chance'
size: 1
point-consumed: 1
max-children: 1
experience-table: skilltree_critical_strike_chance1
coordinates:
@ -71,6 +75,7 @@ nodes:
b2:
name: 'Critical Strike Chance'
size: 1
point-consumed: 1
max-children: 1
experience-table: skilltree_critical_strike_chance2
coordinates:
@ -85,6 +90,7 @@ nodes:
b3:
name: 'Critical Strike Chance'
size: 1
point-consumed: 1
max-children: 2
experience-table: skilltree_critical_strike_chance3
coordinates:
@ -101,6 +107,7 @@ nodes:
c1:
name: 'Life Steal'
size: 1
point-consumed: 1
max-children: 1
experience-table: skilltree_lifesteal1
coordinates:
@ -115,6 +122,7 @@ nodes:
c2:
name: 'Life Steal'
size: 1
point-consumed: 1
max-children: 1
experience-table: skilltree_lifesteal2
coordinates:
@ -129,6 +137,7 @@ nodes:
c3:
name: 'Life Steal'
size: 1
point-consumed: 1
max-children: 1
experience-table: skilltree_lifesteal2
coordinates:
@ -145,6 +154,7 @@ nodes:
d1:
name: 'Damage Reduction'
size: 1
point-consumed: 1
max-children: 1
experience-table: skilltree_damage_reduction1
coordinates:
@ -159,6 +169,7 @@ nodes:
d2:
name: 'Damage Reduction'
size: 1
point-consumed: 1
max-children: 2
experience-table: skilltree_damage_reduction2
coordinates:
@ -173,6 +184,7 @@ nodes:
d3:
name: 'Damage Reduction'
size: 1
point-consumed: 1
max-children: 1
experience-table: skilltree_damage_reduction2
coordinates:
@ -189,6 +201,7 @@ nodes:
e1:
name: 'Health Regeneration'
size: 1
point-consumed: 1
max-children: 1
experience-table: skilltree_health_regeneration1
coordinates:
@ -203,6 +216,7 @@ nodes:
e2:
name: 'Health Regeneration'
size: 1
point-consumed: 1
max-children: 1
experience-table: skilltree_health_regeneration2
coordinates:
@ -217,6 +231,7 @@ nodes:
e3:
name: 'Health Regeneration'
size: 1
point-consumed: 1
max-children: 1
experience-table: skilltree_health_regeneration2
coordinates:
@ -233,6 +248,7 @@ nodes:
f1:
name: 'Mana Regeneration'
size: 1
point-consumed: 1
max-children: 1
experience-table: skilltree_mana_regeneration1
coordinates:
@ -247,6 +263,7 @@ nodes:
f2:
name: 'Mana Regeneration'
size: 1
point-consumed: 1
max-children: 1
experience-table: skilltree_mana_regeneration2
coordinates:
@ -261,6 +278,7 @@ nodes:
f3:
name: 'Mana Regeneration'
size: 1
point-consumed: 1
max-children: 1
experience-table: skilltree_mana_regeneration2
coordinates:
@ -277,6 +295,7 @@ nodes:
g1:
name: 'Magic Damage'
size: 1
point-consumed: 1
max-children: 1
experience-table: skilltree_magic_damage1
coordinates:
@ -291,6 +310,7 @@ nodes:
g2:
name: 'Magic Damage'
size: 1
point-consumed: 1
max-children: 1
experience-table: skilltree_magic_damage2
coordinates:
@ -305,6 +325,7 @@ nodes:
g3:
name: 'Magic Damage'
size: 1
point-consumed: 1
max-children: 1
experience-table: skilltree_magic_damage2
coordinates:
@ -321,6 +342,7 @@ nodes:
h1:
name: 'Max Health'
size: 1
point-consumed: 1
max-children: 1
experience-table: skilltree_max_health2
coordinates:
@ -335,6 +357,7 @@ nodes:
h2:
name: 'Max Health'
size: 1
point-consumed: 1
max-children: 1
experience-table: skilltree_max_health4
coordinates:
@ -349,6 +372,7 @@ nodes:
h3:
name: 'Max Health'
size: 1
point-consumed: 1
max-children: 1
experience-table: skilltree_max_health4
coordinates:
@ -365,6 +389,7 @@ nodes:
j1:
name: 'Weapon Damage'
size: 1
point-consumed: 1
max-children: 1
experience-table: skilltree_weapon_damage2
coordinates:
@ -379,6 +404,7 @@ nodes:
j2:
name: 'Weapon Damage'
size: 1
point-consumed: 1
max-children: 1
experience-table: skilltree_weapon_damage4
coordinates:
@ -393,6 +419,7 @@ nodes:
j3:
name: 'Weapon Damage'
size: 1
point-consumed: 1
max-children: 1
experience-table: skilltree_weapon_damage4
coordinates:

View File

@ -18,6 +18,7 @@ nodes:
max-level: 1
is-root: true
size: 1
point-consumed: 1
experience-table: skilltree_mana_regeneration1
lores:
0:
@ -36,6 +37,8 @@ nodes:
max-level: 1
size: 1
point-consumed: 1
point-consumed: 1
experience-table: skilltree_mana_regeneration2
lores:
0:
@ -54,6 +57,8 @@ nodes:
max-level: 1
size: 1
point-consumed: 1
point-consumed: 1
experience-table: skilltree_mana_regeneration2
lores:
0:
@ -71,6 +76,8 @@ nodes:
max-level: 1
is-root: true
size: 1
point-consumed: 1
point-consumed: 1
experience-table: skilltree_health_regeneration1
lores:
0:
@ -90,6 +97,8 @@ nodes:
max-level: 1
size: 1
point-consumed: 1
point-consumed: 1
experience-table: skilltree_health_regeneration2
lores:
0:
@ -109,6 +118,8 @@ nodes:
max-level: 1
size: 1
point-consumed: 1
point-consumed: 1
experience-table: skilltree_health_regeneration2
lores:
0:
@ -126,6 +137,8 @@ nodes:
max-level: 1
is-root: true
size: 1
point-consumed: 1
point-consumed: 1
experience-table: skilltree_cooldown_reduction5
lores:
0:
@ -145,6 +158,8 @@ nodes:
max-level: 1
size: 1
point-consumed: 1
point-consumed: 1
experience-table: skilltree_cooldown_reduction10
lores:
0:
@ -164,6 +179,8 @@ nodes:
max-level: 1
size: 1
point-consumed: 1
point-consumed: 1
experience-table: skilltree_cooldown_reduction15
lores:
0:
@ -186,6 +203,8 @@ nodes:
max-level: 1
size: 1
point-consumed: 1
point-consumed: 1
experience-table: skilltree_critical_strike_chance1
lores:
0:
@ -205,6 +224,8 @@ nodes:
max-level: 1
size: 1
point-consumed: 1
point-consumed: 1
experience-table: skilltree_critical_strike_chance2
lores:
0:
@ -224,6 +245,8 @@ nodes:
max-level: 1
size: 1
point-consumed: 1
point-consumed: 1
experience-table: skilltree_critical_strike_chance5
lores:
0:
@ -245,6 +268,8 @@ nodes:
max-level: 1
size: 1
point-consumed: 1
point-consumed: 1
experience-table: skilltree_damage_reduction1
lores:
0:
@ -264,6 +289,8 @@ nodes:
max-level: 1
size: 1
point-consumed: 1
point-consumed: 1
experience-table: skilltree_damage_reduction2
lores:
0:
@ -283,6 +310,8 @@ nodes:
max-level: 1
size: 1
point-consumed: 1
point-consumed: 1
experience-table: skilltree_damage_reduction2
lores:
0:
@ -303,6 +332,8 @@ nodes:
max-level: 1
size: 1
point-consumed: 1
point-consumed: 1
experience-table: skilltree_weapon_damage1
lores:
0:
@ -322,6 +353,8 @@ nodes:
max-level: 1
size: 1
point-consumed: 1
point-consumed: 1
experience-table: skilltree_weapon_damage2
lores:
0:
@ -341,6 +374,8 @@ nodes:
max-level: 1
size: 1
point-consumed: 1
point-consumed: 1
experience-table: skilltree_weapon_damage2
lores:
0:
@ -362,6 +397,8 @@ nodes:
max-level: 1
size: 1
point-consumed: 1
point-consumed: 1
experience-table: skilltree_lifesteal3
lores:
0:
@ -382,6 +419,8 @@ nodes:
max-level: 1
size: 1
point-consumed: 1
point-consumed: 1
experience-table: skilltree_max_health5
lores:
0:
@ -401,6 +440,8 @@ nodes:
max-level: 1
size: 1
point-consumed: 1
point-consumed: 1
experience-table: skilltree_magic_damage3
lores:
0:

View File

@ -18,6 +18,7 @@ nodes:
max-level: 1
is-root: true
size: 1
point-consumed: 1
experience-table: skilltree_mana_regeneration1
lores:
0:
@ -36,6 +37,7 @@ nodes:
max-level: 1
size: 1
point-consumed: 1
experience-table: skilltree_mana_regeneration2
lores:
0:
@ -54,6 +56,7 @@ nodes:
max-level: 1
size: 1
point-consumed: 1
experience-table: skilltree_mana_regeneration2
lores:
0:
@ -75,6 +78,7 @@ nodes:
max-level: 1
size: 1
point-consumed: 1
experience-table: skilltree_skill_damage5
lores:
0:
@ -93,6 +97,7 @@ nodes:
max-level: 1
size: 1
point-consumed: 1
experience-table: skilltree_skill_damage5
lores:
0:
@ -111,6 +116,7 @@ nodes:
max-level: 1
size: 1
point-consumed: 1
experience-table: skilltree_skill_damage10
lores:
0:
@ -132,6 +138,7 @@ nodes:
max-level: 1
size: 1
point-consumed: 1
experience-table: skilltree_spell_vampirism2
lores:
0:
@ -150,6 +157,7 @@ nodes:
max-level: 1
size: 1
point-consumed: 1
experience-table: skilltree_spell_vampirism2
lores:
0:
@ -168,6 +176,7 @@ nodes:
max-level: 1
size: 1
point-consumed: 1
experience-table: skilltree_spell_vampirism6
lores:
0:
@ -189,6 +198,7 @@ nodes:
max-level: 1
size: 1
point-consumed: 1
experience-table: skilltree_additional_experience2
lores:
0:
@ -207,6 +217,7 @@ nodes:
max-level: 1
size: 1
point-consumed: 1
experience-table: skilltree_additional_experience3
lores:
0:
@ -225,6 +236,7 @@ nodes:
max-level: 1
size: 1
point-consumed: 1
experience-table: skilltree_additional_experience5
lores:
0:
@ -246,6 +258,7 @@ nodes:
max-level: 1
size: 1
point-consumed: 1
experience-table: skilltree_magic_damage_reduction5
lores:
0:
@ -264,6 +277,7 @@ nodes:
max-level: 1
size: 1
point-consumed: 1
experience-table: skilltree_magic_damage_reduction5
lores:
0:
@ -282,6 +296,7 @@ nodes:
max-level: 1
size: 1
point-consumed: 1
experience-table: skilltree_magic_damage_reduction10
lores:
0:
@ -303,6 +318,7 @@ nodes:
max-level: 1
size: 1
point-consumed: 1
experience-table: skilltree_skill_critical_strike_chance5
lores:
0:
@ -321,6 +337,7 @@ nodes:
max-level: 1
size: 1
point-consumed: 1
experience-table: skilltree_skill_critical_strike_chance10
lores:
0:
@ -339,6 +356,7 @@ nodes:
max-level: 1
size: 1
point-consumed: 1
experience-table: skilltree_skill_critical_strike_chance15
lores:
0:
@ -360,6 +378,7 @@ nodes:
max-level: 1
size: 1
point-consumed: 1
experience-table: skilltree_magic_damage1
lores:
0:
@ -378,6 +397,7 @@ nodes:
max-level: 1
size: 1
point-consumed: 1
experience-table: skilltree_magic_damage2
lores:
0:
@ -396,6 +416,7 @@ nodes:
max-level: 1
size: 1
point-consumed: 1
experience-table: skilltree_magic_damage2
lores:
0:

View File

@ -18,6 +18,7 @@ nodes:
max-level: 1
is-root: true
size: 1
point-consumed: 1
experience-table: skilltree_additional_experience2
lores:
0:
@ -36,6 +37,7 @@ nodes:
max-level: 1
size: 1
point-consumed: 1
experience-table: skilltree_additional_experience3
lores:
0:
@ -54,6 +56,7 @@ nodes:
max-level: 1
size: 1
point-consumed: 1
experience-table: skilltree_additional_experience5
lores:
0:
@ -75,6 +78,7 @@ nodes:
max-level: 1
size: 1
point-consumed: 1
experience-table: skilltree_arrow_velocity10
lores:
0:
@ -93,6 +97,7 @@ nodes:
max-level: 1
size: 1
point-consumed: 1
experience-table: skilltree_arrow_velocity15
lores:
0:
@ -111,6 +116,7 @@ nodes:
max-level: 1
size: 1
point-consumed: 1
experience-table: skilltree_arrow_velocity25
lores:
0:
@ -132,6 +138,7 @@ nodes:
max-level: 1
size: 1
point-consumed: 1
experience-table: skilltree_lifesteal1
lores:
0:
@ -150,6 +157,7 @@ nodes:
max-level: 1
size: 1
point-consumed: 1
experience-table: skilltree_lifesteal2
lores:
0:
@ -168,6 +176,7 @@ nodes:
max-level: 1
size: 1
point-consumed: 1
experience-table: skilltree_lifesteal2
lores:
0:
@ -189,6 +198,7 @@ nodes:
max-level: 1
size: 1
point-consumed: 1
experience-table: skilltree_knockback_resistance5
lores:
0:
@ -207,6 +217,7 @@ nodes:
max-level: 1
size: 1
point-consumed: 1
experience-table: skilltree_knockback_resistance5
lores:
0:
@ -225,6 +236,7 @@ nodes:
max-level: 1
size: 1
point-consumed: 1
experience-table: skilltree_knockback_resistance10
lores:
0:
@ -246,6 +258,7 @@ nodes:
max-level: 1
size: 1
point-consumed: 1
experience-table: skilltree_critical_strike_chance2
lores:
0:
@ -264,6 +277,7 @@ nodes:
max-level: 1
size: 1
point-consumed: 1
experience-table: skilltree_critical_strike_chance3
lores:
0:
@ -282,6 +296,7 @@ nodes:
max-level: 1
size: 1
point-consumed: 1
experience-table: skilltree_critical_strike_chance5
lores:
0:
@ -303,6 +318,7 @@ nodes:
max-level: 1
size: 1
point-consumed: 1
experience-table: skilltree_movement_speed2
lores:
0:
@ -321,6 +337,7 @@ nodes:
max-level: 1
size: 1
point-consumed: 1
experience-table: skilltree_movement_speed2
lores:
0:
@ -339,6 +356,7 @@ nodes:
max-level: 1
size: 1
point-consumed: 1
experience-table: skilltree_movement_speed6
lores:
0:
@ -360,6 +378,7 @@ nodes:
max-level: 1
size: 1
point-consumed: 1
experience-table: skilltree_projectile_damage2
lores:
0:
@ -378,6 +397,7 @@ nodes:
max-level: 1
size: 1
point-consumed: 1
experience-table: skilltree_projectile_damage2
lores:
0:
@ -396,6 +416,7 @@ nodes:
max-level: 1
size: 1
point-consumed: 1
experience-table: skilltree_projectile_damage6
lores:
0:

View File

@ -18,6 +18,7 @@ nodes:
max-level: 1
is-root: true
size: 1
point-consumed: 1
experience-table: skilltree_attack_speed5
lores:
0:
@ -36,6 +37,7 @@ nodes:
max-level: 1
size: 1
point-consumed: 1
experience-table: skilltree_attack_speed10
lores:
0:
@ -54,6 +56,7 @@ nodes:
max-level: 1
size: 1
point-consumed: 1
experience-table: skilltree_attack_speed15
lores:
0:
@ -75,6 +78,7 @@ nodes:
max-level: 1
size: 1
point-consumed: 1
experience-table: skilltree_health_regeneration1
lores:
0:
@ -93,6 +97,7 @@ nodes:
max-level: 1
size: 1
point-consumed: 1
experience-table: skilltree_health_regeneration2
lores:
0:
@ -111,6 +116,7 @@ nodes:
max-level: 1
size: 1
point-consumed: 1
experience-table: skilltree_health_regeneration2
lores:
0:
@ -132,6 +138,7 @@ nodes:
max-level: 1
size: 1
point-consumed: 1
experience-table: skilltree_physical_damage_reduction5
lores:
0:
@ -150,6 +157,7 @@ nodes:
max-level: 1
size: 1
point-consumed: 1
experience-table: skilltree_physical_damage_reduction10
lores:
0:
@ -168,6 +176,7 @@ nodes:
max-level: 1
size: 1
point-consumed: 1
experience-table: skilltree_physical_damage_reduction15
lores:
0:
@ -189,6 +198,7 @@ nodes:
max-level: 1
size: 1
point-consumed: 1
experience-table: skilltree_parry_rating5
lores:
0:
@ -207,6 +217,7 @@ nodes:
max-level: 1
size: 1
point-consumed: 1
experience-table: skilltree_parry_rating5
lores:
0:
@ -225,6 +236,7 @@ nodes:
max-level: 1
size: 1
point-consumed: 1
experience-table: skilltree_parry_rating10
lores:
0:
@ -246,6 +258,7 @@ nodes:
max-level: 1
size: 1
point-consumed: 1
experience-table: skilltree_knockback_resistance5
lores:
0:
@ -264,6 +277,7 @@ nodes:
max-level: 1
size: 1
point-consumed: 1
experience-table: skilltree_knockback_resistance5
lores:
0:
@ -282,6 +296,7 @@ nodes:
max-level: 1
size: 1
point-consumed: 1
experience-table: skilltree_knockback_resistance10
lores:
0:
@ -303,6 +318,7 @@ nodes:
max-level: 1
size: 1
point-consumed: 1
experience-table: skilltree_dodge_rating2
lores:
0:
@ -321,6 +337,7 @@ nodes:
max-level: 1
size: 1
point-consumed: 1
experience-table: skilltree_dodge_rating3
lores:
0:
@ -339,6 +356,7 @@ nodes:
max-level: 1
size: 1
point-consumed: 1
experience-table: skilltree_dodge_rating5
lores:
0:
@ -360,6 +378,7 @@ nodes:
max-level: 1
size: 1
point-consumed: 1
experience-table: skilltree_weapon_damage1
lores:
0:
@ -378,6 +397,7 @@ nodes:
max-level: 1
size: 1
point-consumed: 1
experience-table: skilltree_weapon_damage2
lores:
0:
@ -396,6 +416,7 @@ nodes:
max-level: 1
size: 1
point-consumed: 1
experience-table: skilltree_weapon_damage2
lores:
0:

View File

@ -27,3 +27,27 @@ permissions:
mmocore.class-select:
description: Access to /class
default: op
mmocore.attributes:
description: Access to /attributes
default: true
mmocore.skills:
description: Access to /skills
default: true
mmocore.profile:
description: Access to /profile
default: true
mmocore.friends:
description: Access to /friends
default: true
mmocore.guild:
description: Access to /guild
default: true
mmocore.party:
description: Access to /party
default: true
mmocore.skilltrees:
description: Access to /skilltrees
default: true
mmocore.quests:
description: Access to /quests
default: true

View File

@ -13,7 +13,7 @@ And then add MMOCore-API as dependency
<dependency>
<groupId>net.Indyuce</groupId>
<artifactId>MMOCore-API</artifactId>
<version>1.12.0-SNAPSHOT</version>
<version>1.12-SNAPSHOT</version>
<scope>provided</scope>
</dependency>
```

View File

@ -3,3 +3,5 @@ files:
translation: /**/MMOCore-Dist/src/main/resources/default/translation/%language%/gui/%file_name%.%file_extension%
- source: MMOCore-Dist/src/main/resources/default/messages.yml
translation: /**/MMOCore-Dist/src/main/resources/default/translation/%language%/%file_name%.%file_extension%
- source: MMOCore-Dist/src/main/resources/default/gui/class-confirm/*.yml
translation: /**/MMOCore-Dist/src/main/resources/default/translation/%language%/gui/class-confirm/%file_name%.%file_extension%