From fece7347ef7b622d4f6c86792fa738039fd16bf0 Mon Sep 17 00:00:00 2001 From: Guillaume Date: Tue, 24 May 2022 15:26:22 +0200 Subject: [PATCH] Skill Trees in MMOCore --- .../java/net/Indyuce/mmocore/MMOCore.java | 812 +++++++++--------- .../mmocore/api/player/PlayerData.java | 32 +- .../mmocore/api/util/MMOCoreUtils.java | 5 + .../mmocore/command/SkillTreeCommand.java | 38 + .../Indyuce/mmocore/gui/SkillTreeViewer.java | 345 ++++++++ .../mmocore/gui/api/item/InventoryItem.java | 8 +- .../mmocore/manager/InventoryManager.java | 64 +- .../mmocore/manager/SkillTreeManager.java | 48 +- .../data/yaml/YAMLPlayerDataManager.java | 227 ++--- .../mmocore/quest/MMOCoreQuestModule.java | 3 +- .../quest/compat/BeautyQuestModule.java | 12 +- .../quest/compat/BlackVeinQuestsModule.java | 9 +- .../quest/compat/QuestCreatorModule.java | 5 +- .../mmocore/quest/compat/QuestModule.java | 5 +- .../net/Indyuce/mmocore/tree/NodeState.java | 5 + .../net/Indyuce/mmocore/tree/SkillTree.java | 79 -- .../Indyuce/mmocore/tree/SkillTreeNode.java | 79 +- .../tree/skilltree/AutomaticSkillTree.java | 167 ++++ .../tree/skilltree/CustomSkillTree.java | 21 + .../tree/skilltree/LinkedSkillTree.java | 64 ++ .../mmocore/tree/skilltree/SkillTree.java | 154 ++++ .../mmocore/tree/skilltree/SkillTreeType.java | 7 + src/main/resources/default/commands.yml | 3 + src/main/resources/default/gui/skill-tree.yml | 25 + .../resources/default/skilltree/combat.yml | 29 + 25 files changed, 1590 insertions(+), 656 deletions(-) create mode 100644 src/main/java/net/Indyuce/mmocore/command/SkillTreeCommand.java create mode 100644 src/main/java/net/Indyuce/mmocore/gui/SkillTreeViewer.java create mode 100644 src/main/java/net/Indyuce/mmocore/tree/NodeState.java delete mode 100644 src/main/java/net/Indyuce/mmocore/tree/SkillTree.java create mode 100644 src/main/java/net/Indyuce/mmocore/tree/skilltree/AutomaticSkillTree.java create mode 100644 src/main/java/net/Indyuce/mmocore/tree/skilltree/CustomSkillTree.java create mode 100644 src/main/java/net/Indyuce/mmocore/tree/skilltree/LinkedSkillTree.java create mode 100644 src/main/java/net/Indyuce/mmocore/tree/skilltree/SkillTree.java create mode 100644 src/main/java/net/Indyuce/mmocore/tree/skilltree/SkillTreeType.java create mode 100644 src/main/resources/default/gui/skill-tree.yml create mode 100644 src/main/resources/default/skilltree/combat.yml diff --git a/src/main/java/net/Indyuce/mmocore/MMOCore.java b/src/main/java/net/Indyuce/mmocore/MMOCore.java index df5b7dcf..16d534f8 100644 --- a/src/main/java/net/Indyuce/mmocore/MMOCore.java +++ b/src/main/java/net/Indyuce/mmocore/MMOCore.java @@ -59,410 +59,410 @@ import java.util.Iterator; import java.util.logging.Level; public class MMOCore extends LuminePlugin { - public static MMOCore plugin; - - public final WaypointManager waypointManager = new WaypointManager(); - public final SoundManager soundManager = new SoundManager(); - public final RequestManager requestManager = new RequestManager(); - public final ConfigItemManager configItems = new ConfigItemManager(); - public final PlayerActionBar actionBarManager = new PlayerActionBar(); - public final SkillManager skillManager = new SkillManager(); - public final ClassManager classManager = new ClassManager(); - public final DropTableManager dropTableManager = new DropTableManager(); - public final BoosterManager boosterManager = new BoosterManager(); - public final AttributeManager attributeManager = new AttributeManager(); - public final PartyManager partyManager = new PartyManager(); - public final QuestManager questManager = new QuestManager(); - public final ProfessionManager professionManager = new ProfessionManager(); - public final ExperienceManager experience = new ExperienceManager(); - public final LootChestManager lootChests = new LootChestManager(); - public final MMOLoadManager loadManager = new MMOLoadManager(); - public final RestrictionManager restrictionManager = new RestrictionManager(); - @Deprecated - public final SkillTreeManager skillTreeManager = new SkillTreeManager(); - - // Profession managers - public final CustomBlockManager mineManager = new CustomBlockManager(); - public final FishingManager fishingManager = new FishingManager(); - public final AlchemyManager alchemyManager = new AlchemyManager(); - public final EnchantManager enchantManager = new EnchantManager(); - public final SmithingManager smithingManager = new SmithingManager(); - - @NotNull - public ConfigManager configManager; - public VaultEconomy economy; - public RegionHandler regionHandler = new DefaultRegionHandler(); - public PlaceholderParser placeholderParser = new DefaultParser(); - public DataProvider dataProvider = new YAMLDataProvider(); - - // Modules - @NotNull - public PartyModule partyModule; - - public boolean shouldDebugSQL = false; - - private static final int MYTHICLIB_COMPATIBILITY_INDEX = 7; - - public MMOCore() { - plugin = this; - } - - public void load() { - - // Check if the ML build matches - if (MYTHICLIB_COMPATIBILITY_INDEX != MythicLib.MMOCORE_COMPATIBILITY_INDEX) { - getLogger().log(Level.WARNING, "Your versions of MythicLib and MMOCore do not match. Make sure you are using the latest builds of both plugins"); - disable(); - return; - } - - // Register target restrictions due to MMOCore in MythicLib - MythicLib.plugin.getEntities().registerRestriction(new MMOCoreTargetRestriction()); - - // Register extra objective, drop items... - if (Bukkit.getPluginManager().getPlugin("WorldGuard") != null) - loadManager.registerLoader(new WorldGuardMMOLoader()); - - if (Bukkit.getPluginManager().getPlugin("Citizens") != null) - loadManager.registerLoader(new CitizensMMOLoader()); - - if (Bukkit.getPluginManager().getPlugin("Vault") != null) loadManager.registerLoader(new VaultMMOLoader()); - - if (Bukkit.getPluginManager().getPlugin("MythicMobs") != null) - loadManager.registerLoader(new MythicMobsMMOLoader()); - } - - public void enable() { - new SpigotPlugin(70575, this).checkForUpdate(); - new Metrics(this); - saveDefaultConfig(); - - final int configVersion = getConfig().contains("config-version", true) ? getConfig().getInt("config-version") : -1; - final int defConfigVersion = getConfig().getDefaults().getInt("config-version"); - if (configVersion != defConfigVersion) { - getLogger().warning("You may be using an outdated config.yml!"); - getLogger().warning("(Your config version: '" + configVersion + "' | Expected config version: '" + defConfigVersion + "')"); - } - - if (getConfig().isConfigurationSection("mysql") && getConfig().getBoolean("mysql.enabled")) - dataProvider = new MySQLDataProvider(getConfig()); - shouldDebugSQL = getConfig().getBoolean("mysql.debug"); - - if (getConfig().isConfigurationSection("default-playerdata")) - dataProvider.getDataManager().loadDefaultData(getConfig().getConfigurationSection("default-playerdata")); - - if (Bukkit.getPluginManager().getPlugin("Vault") != null) economy = new VaultEconomy(); - - if (Bukkit.getPluginManager().getPlugin("PlaceholderAPI") != null) { - placeholderParser = new PlaceholderAPIParser(); - getLogger().log(Level.INFO, "Hooked onto PlaceholderAPI"); - } - - if (Bukkit.getPluginManager().getPlugin("Citizens") != null) { - Bukkit.getPluginManager().registerEvents(new CitizenInteractEventListener(), this); - getLogger().log(Level.INFO, "Hooked onto Citizens"); - } - - if (Bukkit.getPluginManager().getPlugin("WorldGuard") != null) { - regionHandler = new WorldGuardRegionHandler(); - getLogger().log(Level.INFO, "Hooked onto WorldGuard"); - } - - if (Bukkit.getPluginManager().getPlugin("MythicMobs") != null) { - Bukkit.getServer().getPluginManager().registerEvents(new MythicHook(), this); - MMOCore.plugin.getLogger().log(Level.INFO, "Hooked onto MythicMobs"); - } - - /* - * Resource regeneration. Must check if entity is dead otherwise regen will make - * the 'respawn' button glitched plus HURT entity effect bug - */ - new BukkitRunnable() { - public void run() { - for (PlayerData player : PlayerData.getAll()) - if (player.isOnline() && !player.getPlayer().isDead()) - for (PlayerResource resource : PlayerResource.values()) { - double regenAmount = player.getProfess().getHandler(resource).getRegen(player); - if (regenAmount != 0) - resource.regen(player, regenAmount); - } - } - }.runTaskTimer(MMOCore.plugin, 100, 20); - - /* - * Clean active loot chests every 5 minutes. Cannot register this runnable in - * the loot chest manager because it is instanced when the plugin loads - */ - new BukkitRunnable() { - public void run() { - Iterator iterator = lootChests.getActive().iterator(); - while (iterator.hasNext()) { - LootChest next = iterator.next(); - if (next.shouldExpire()) { - iterator.remove(); - next.expire(false); - } - } - } - }.runTaskTimer(this, 5 * 60 * 20, 5 * 60 * 20); - - /* - * For the sake of the lord, make sure they aren't using MMOItems Mana and - * Stamina Addon...This should prevent a couple error reports produced by people - * not reading the installation guide... - */ - if (Bukkit.getPluginManager().getPlugin("MMOMana") != null) { - getLogger().log(Level.SEVERE, ChatColor.DARK_RED + "MMOCore is not meant to be used with MMOItems ManaAndStamina"); - getLogger().log(Level.SEVERE, ChatColor.DARK_RED + "Please read the installation guide!"); - Bukkit.broadcastMessage(ChatColor.DARK_RED + "[MMOCore] MMOCore is not meant to be used with MMOItems ManaAndStamina"); - Bukkit.broadcastMessage(ChatColor.DARK_RED + "[MMOCore] Please read the installation guide!"); - return; - } - - initializePlugin(false); - - if (getConfig().getBoolean("vanilla-exp-redirection.enabled")) - Bukkit.getPluginManager().registerEvents(new RedirectVanillaExp(getConfig().getDouble("vanilla-exp-redirection.ratio")), this); - - // Enable debug mode for extra debug tools - if (getConfig().contains("debug")) { - DebugMode.setLevel(getConfig().getInt("debug", 0)); - DebugMode.enableActionBar(); - } - - // Load quest module - try { - String questPluginName = UtilityMethods.enumName(getConfig().getString("quest-plugin")); - PartyModuleType moduleType = PartyModuleType.valueOf(questPluginName); - Validate.isTrue(moduleType.isValid(), "Plugin '" + moduleType.name() + "' is not installed"); - partyModule = moduleType.provideModule(); - } catch (RuntimeException exception) { - getLogger().log(Level.WARNING, "Could not initialize quest module: " + exception.getMessage()); - partyModule = new MMOCorePartyModule(); - } - - - - // Load party module - try { - String partyPluginName = UtilityMethods.enumName(getConfig().getString("party-plugin")); - PartyModuleType moduleType = PartyModuleType.valueOf(partyPluginName); - Validate.isTrue(moduleType.isValid(), "Plugin '" + moduleType.name() + "' is not installed"); - partyModule = moduleType.provideModule(); - } catch (RuntimeException exception) { - getLogger().log(Level.WARNING, "Could not initialize party module: " + exception.getMessage()); - partyModule = new MMOCorePartyModule(); - } - - - // Skill casting - try { - SkillCastingMode mode = SkillCastingMode.valueOf(UtilityMethods.enumName(getConfig().getString("skill-casting.mode"))); - Bukkit.getPluginManager().registerEvents(mode.loadFromConfig(getConfig().getConfigurationSection("skill-casting")), this); - } catch (RuntimeException exception) { - getLogger().log(Level.WARNING, "Could not load skill casting: " + exception.getMessage()); - } - - if (configManager.overrideVanillaExp = getConfig().getBoolean("override-vanilla-exp")) - Bukkit.getPluginManager().registerEvents(new VanillaExperienceOverride(), this); - - if (getConfig().getBoolean("hotbar-swapping.enabled")) - try { - Bukkit.getPluginManager().registerEvents(new HotbarSwap(getConfig().getConfigurationSection("hotbar-swapping")), this); - } catch (RuntimeException exception) { - getLogger().log(Level.WARNING, "Could not load hotbar swapping: " + exception.getMessage()); - } - - if (getConfig().getBoolean("prevent-spawner-xp")) - Bukkit.getPluginManager().registerEvents(new NoSpawnerEXP(), this); - - if (getConfig().getBoolean("death-exp-loss.enabled")) - Bukkit.getPluginManager().registerEvents(new DeathExperienceLoss(), this); - - if (getConfig().getBoolean("shift-click-player-profile-check")) - Bukkit.getPluginManager().registerEvents(new PlayerProfileCheck(), this); - - Bukkit.getPluginManager().registerEvents(new WaypointsListener(), this); - Bukkit.getPluginManager().registerEvents(new PlayerListener(), this); - Bukkit.getPluginManager().registerEvents(new GoldPouchesListener(), this); - Bukkit.getPluginManager().registerEvents(new BlockListener(), this); - Bukkit.getPluginManager().registerEvents(new LootableChestsListener(), this); - Bukkit.getPluginManager().registerEvents(new GuildListener(), this); - Bukkit.getPluginManager().registerEvents(new FishingListener(), this); - Bukkit.getPluginManager().registerEvents(new PlayerCollectStats(), this); - Bukkit.getPluginManager().registerEvents(new PlayerPressKeyListener(), this); - // Bukkit.getPluginManager().registerEvents(new ClassTriggers(), this); - - /* - * Initialize player data from all online players. This is very important to do - * that after registering all the professses otherwise the player datas can't - * recognize what profess the player has and professes will be lost - */ - Bukkit.getOnlinePlayers().forEach(player -> dataProvider.getDataManager().setup(player.getUniqueId())); - - // load guild data after loading player data - dataProvider.getGuildManager().load(); - - // Command - try { - final Field bukkitCommandMap = Bukkit.getServer().getClass().getDeclaredField("commandMap"); - - bukkitCommandMap.setAccessible(true); - CommandMap commandMap = (CommandMap) bukkitCommandMap.get(Bukkit.getServer()); - - FileConfiguration config = new ConfigFile("commands").getConfig(); - - if (config.contains("player")) - commandMap.register("mmocore", new PlayerStatsCommand(config.getConfigurationSection("player"))); - if (config.contains("attributes")) - commandMap.register("mmocore", new AttributesCommand(config.getConfigurationSection("attributes"))); - if (config.contains("class")) - commandMap.register("mmocore", new ClassCommand(config.getConfigurationSection("class"))); - if (config.contains("waypoints")) - commandMap.register("mmocore", new WaypointsCommand(config.getConfigurationSection("waypoints"))); - if (config.contains("quests")) - commandMap.register("mmocore", new QuestsCommand(config.getConfigurationSection("quests"))); - if (config.contains("skills")) - commandMap.register("mmocore", new SkillsCommand(config.getConfigurationSection("skills"))); - if (config.contains("friends")) - commandMap.register("mmocore", new FriendsCommand(config.getConfigurationSection("friends"))); - if (config.contains("party")) - commandMap.register("mmocore", new PartyCommand(config.getConfigurationSection("party"))); - if (config.contains("guild")) - commandMap.register("mmocore", new GuildCommand(config.getConfigurationSection("guild"))); - - if (hasEconomy() && economy.isValid()) { - if (config.contains("withdraw")) - commandMap.register("mmocore", new WithdrawCommand(config.getConfigurationSection("withdraw"))); - if (config.contains("deposit")) - commandMap.register("mmocore", new DepositCommand(config.getConfigurationSection("deposit"))); - } - } catch (NoSuchFieldException | IllegalArgumentException | IllegalAccessException ex) { - ex.printStackTrace(); - } - - MMOCoreCommandTreeRoot mmoCoreCommand = new MMOCoreCommandTreeRoot(); - getCommand("mmocore").setExecutor(mmoCoreCommand); - getCommand("mmocore").setTabCompleter(mmoCoreCommand); - - if (getConfig().getBoolean("auto-save.enabled")) { - int autosave = getConfig().getInt("auto-save.interval") * 20; - new BukkitRunnable() { - public void run() { - - // Save player data - for (PlayerData data : PlayerData.getAll()) - if (data.isFullyLoaded()) - dataProvider.getDataManager().saveData(data); - - // Save guild info - for (Guild guild : dataProvider.getGuildManager().getAll()) - dataProvider.getGuildManager().save(guild); - } - }.runTaskTimerAsynchronously(MMOCore.plugin, autosave, autosave); - } - } - - public void disable() { - - // Save player data - for (PlayerData data : PlayerData.getAll()) - if (data.isFullyLoaded()) { - data.close(); - dataProvider.getDataManager().saveData(data); - } - - // Save guild info - for (Guild guild : dataProvider.getGuildManager().getAll()) - dataProvider.getGuildManager().save(guild); - - // Close MySQL data provider (memory leaks) - if (dataProvider instanceof MySQLDataProvider) - ((MySQLDataProvider) dataProvider).close(); - - // Reset active blocks - mineManager.resetRemainingBlocks(); - - // Clear spawned loot chests - lootChests.getActive().forEach(chest -> chest.expire(false)); - } - - /** - * Called either when the server starts when initializing the manager for - * the first time, or when issuing a plugin reload; in that case, stuff - * like listeners must all be cleared before. - * - * Also see {@link MMOCoreManager} - * - * @param clearBefore True when issuing a plugin reload - */ - public void initializePlugin(boolean clearBefore) { - if (clearBefore) - reloadConfig(); - - configManager = new ConfigManager(); - - if (clearBefore) - MythicLib.plugin.getSkills().initialize(true); - skillManager.initialize(clearBefore); - mineManager.initialize(clearBefore); - partyManager.initialize(clearBefore); - attributeManager.initialize(clearBefore); - - // Experience must be loaded before professions and classes - experience.initialize(clearBefore); - - // Drop tables must be loaded before professions - dropTableManager.initialize(clearBefore); - - professionManager.initialize(clearBefore); - classManager.initialize(clearBefore); - - InventoryManager.load(); - - questManager.initialize(clearBefore); - lootChests.initialize(clearBefore); - restrictionManager.initialize(clearBefore); - waypointManager.initialize(clearBefore); - requestManager.initialize(clearBefore); - soundManager.initialize(clearBefore); - configItems.initialize(clearBefore); - - if (getConfig().isConfigurationSection("action-bar")) - actionBarManager.reload(getConfig().getConfigurationSection("action-bar")); - - StatType.load(); - - if (clearBefore) - PlayerData.getAll().forEach(PlayerData::update); - } - - public static void log(String message) { - log(Level.INFO, message); - } - - public static void debug(int value, String message) { - debug(value, Level.INFO, message); - } - - public static void log(Level level, String message) { - plugin.getLogger().log(level, message); - } - - public static void debug(int value, Level level, String message) { - if (DebugMode.level > (value - 1)) plugin.getLogger().log(level, message); - } - - public File getJarFile() { - return getFile(); - } - - public boolean hasEconomy() { - return economy != null && economy.isValid(); - } - - public static void sqlDebug(String s) { - if(!MMOCore.plugin.shouldDebugSQL) return; - MMOCore.plugin.getLogger().warning("- [SQL Debug] " + s); - } + public static MMOCore plugin; + + public final WaypointManager waypointManager = new WaypointManager(); + public final SoundManager soundManager = new SoundManager(); + public final RequestManager requestManager = new RequestManager(); + public final ConfigItemManager configItems = new ConfigItemManager(); + public final PlayerActionBar actionBarManager = new PlayerActionBar(); + public final SkillManager skillManager = new SkillManager(); + public final ClassManager classManager = new ClassManager(); + public final DropTableManager dropTableManager = new DropTableManager(); + public final BoosterManager boosterManager = new BoosterManager(); + public final AttributeManager attributeManager = new AttributeManager(); + public final PartyManager partyManager = new PartyManager(); + public final QuestManager questManager = new QuestManager(); + public final ProfessionManager professionManager = new ProfessionManager(); + public final ExperienceManager experience = new ExperienceManager(); + public final LootChestManager lootChests = new LootChestManager(); + public final MMOLoadManager loadManager = new MMOLoadManager(); + public final RestrictionManager restrictionManager = new RestrictionManager(); + public final SkillTreeManager skillTreeManager = new SkillTreeManager(); + + // Profession managers + public final CustomBlockManager mineManager = new CustomBlockManager(); + public final FishingManager fishingManager = new FishingManager(); + public final AlchemyManager alchemyManager = new AlchemyManager(); + public final EnchantManager enchantManager = new EnchantManager(); + public final SmithingManager smithingManager = new SmithingManager(); + + @NotNull + public ConfigManager configManager; + public VaultEconomy economy; + public RegionHandler regionHandler = new DefaultRegionHandler(); + public PlaceholderParser placeholderParser = new DefaultParser(); + public DataProvider dataProvider = new YAMLDataProvider(); + + // Modules + @NotNull + public PartyModule partyModule; + + public boolean shouldDebugSQL = false; + + private static final int MYTHICLIB_COMPATIBILITY_INDEX = 7; + + public MMOCore() { + plugin = this; + } + + public void load() { + + // Check if the ML build matches + if (MYTHICLIB_COMPATIBILITY_INDEX != MythicLib.MMOCORE_COMPATIBILITY_INDEX) { + getLogger().log(Level.WARNING, "Your versions of MythicLib and MMOCore do not match. Make sure you are using the latest builds of both plugins"); + disable(); + return; + } + + // Register target restrictions due to MMOCore in MythicLib + MythicLib.plugin.getEntities().registerRestriction(new MMOCoreTargetRestriction()); + + // Register extra objective, drop items... + if (Bukkit.getPluginManager().getPlugin("WorldGuard") != null) + loadManager.registerLoader(new WorldGuardMMOLoader()); + + if (Bukkit.getPluginManager().getPlugin("Citizens") != null) + loadManager.registerLoader(new CitizensMMOLoader()); + + if (Bukkit.getPluginManager().getPlugin("Vault") != null) loadManager.registerLoader(new VaultMMOLoader()); + + if (Bukkit.getPluginManager().getPlugin("MythicMobs") != null) + loadManager.registerLoader(new MythicMobsMMOLoader()); + } + + public void enable() { + new SpigotPlugin(70575, this).checkForUpdate(); + new Metrics(this); + saveDefaultConfig(); + + final int configVersion = getConfig().contains("config-version", true) ? getConfig().getInt("config-version") : -1; + final int defConfigVersion = getConfig().getDefaults().getInt("config-version"); + if (configVersion != defConfigVersion) { + getLogger().warning("You may be using an outdated config.yml!"); + getLogger().warning("(Your config version: '" + configVersion + "' | Expected config version: '" + defConfigVersion + "')"); + } + + if (getConfig().isConfigurationSection("mysql") && getConfig().getBoolean("mysql.enabled")) + dataProvider = new MySQLDataProvider(getConfig()); + shouldDebugSQL = getConfig().getBoolean("mysql.debug"); + + if (getConfig().isConfigurationSection("default-playerdata")) + dataProvider.getDataManager().loadDefaultData(getConfig().getConfigurationSection("default-playerdata")); + + if (Bukkit.getPluginManager().getPlugin("Vault") != null) economy = new VaultEconomy(); + + if (Bukkit.getPluginManager().getPlugin("PlaceholderAPI") != null) { + placeholderParser = new PlaceholderAPIParser(); + getLogger().log(Level.INFO, "Hooked onto PlaceholderAPI"); + } + + if (Bukkit.getPluginManager().getPlugin("Citizens") != null) { + Bukkit.getPluginManager().registerEvents(new CitizenInteractEventListener(), this); + getLogger().log(Level.INFO, "Hooked onto Citizens"); + } + + if (Bukkit.getPluginManager().getPlugin("WorldGuard") != null) { + regionHandler = new WorldGuardRegionHandler(); + getLogger().log(Level.INFO, "Hooked onto WorldGuard"); + } + + if (Bukkit.getPluginManager().getPlugin("MythicMobs") != null) { + Bukkit.getServer().getPluginManager().registerEvents(new MythicHook(), this); + MMOCore.plugin.getLogger().log(Level.INFO, "Hooked onto MythicMobs"); + } + + /* + * Resource regeneration. Must check if entity is dead otherwise regen will make + * the 'respawn' button glitched plus HURT entity effect bug + */ + new BukkitRunnable() { + public void run() { + for (PlayerData player : PlayerData.getAll()) + if (player.isOnline() && !player.getPlayer().isDead()) + for (PlayerResource resource : PlayerResource.values()) { + double regenAmount = player.getProfess().getHandler(resource).getRegen(player); + if (regenAmount != 0) + resource.regen(player, regenAmount); + } + } + }.runTaskTimer(MMOCore.plugin, 100, 20); + + /* + * Clean active loot chests every 5 minutes. Cannot register this runnable in + * the loot chest manager because it is instanced when the plugin loads + */ + new BukkitRunnable() { + public void run() { + Iterator iterator = lootChests.getActive().iterator(); + while (iterator.hasNext()) { + LootChest next = iterator.next(); + if (next.shouldExpire()) { + iterator.remove(); + next.expire(false); + } + } + } + }.runTaskTimer(this, 5 * 60 * 20, 5 * 60 * 20); + + /* + * For the sake of the lord, make sure they aren't using MMOItems Mana and + * Stamina Addon...This should prevent a couple error reports produced by people + * not reading the installation guide... + */ + if (Bukkit.getPluginManager().getPlugin("MMOMana") != null) { + getLogger().log(Level.SEVERE, ChatColor.DARK_RED + "MMOCore is not meant to be used with MMOItems ManaAndStamina"); + getLogger().log(Level.SEVERE, ChatColor.DARK_RED + "Please read the installation guide!"); + Bukkit.broadcastMessage(ChatColor.DARK_RED + "[MMOCore] MMOCore is not meant to be used with MMOItems ManaAndStamina"); + Bukkit.broadcastMessage(ChatColor.DARK_RED + "[MMOCore] Please read the installation guide!"); + return; + } + + initializePlugin(false); + + if (getConfig().getBoolean("vanilla-exp-redirection.enabled")) + Bukkit.getPluginManager().registerEvents(new RedirectVanillaExp(getConfig().getDouble("vanilla-exp-redirection.ratio")), this); + + // Enable debug mode for extra debug tools + if (getConfig().contains("debug")) { + DebugMode.setLevel(getConfig().getInt("debug", 0)); + DebugMode.enableActionBar(); + } + + // Load quest module + try { + String questPluginName = UtilityMethods.enumName(getConfig().getString("quest-plugin")); + PartyModuleType moduleType = PartyModuleType.valueOf(questPluginName); + Validate.isTrue(moduleType.isValid(), "Plugin '" + moduleType.name() + "' is not installed"); + partyModule = moduleType.provideModule(); + } catch (RuntimeException exception) { + getLogger().log(Level.WARNING, "Could not initialize quest module: " + exception.getMessage()); + partyModule = new MMOCorePartyModule(); + } + + + // Load party module + try { + String partyPluginName = UtilityMethods.enumName(getConfig().getString("party-plugin")); + PartyModuleType moduleType = PartyModuleType.valueOf(partyPluginName); + Validate.isTrue(moduleType.isValid(), "Plugin '" + moduleType.name() + "' is not installed"); + partyModule = moduleType.provideModule(); + } catch (RuntimeException exception) { + getLogger().log(Level.WARNING, "Could not initialize party module: " + exception.getMessage()); + partyModule = new MMOCorePartyModule(); + } + + + // Skill casting + try { + SkillCastingMode mode = SkillCastingMode.valueOf(UtilityMethods.enumName(getConfig().getString("skill-casting.mode"))); + Bukkit.getPluginManager().registerEvents(mode.loadFromConfig(getConfig().getConfigurationSection("skill-casting")), this); + } catch (RuntimeException exception) { + getLogger().log(Level.WARNING, "Could not load skill casting: " + exception.getMessage()); + } + + if (configManager.overrideVanillaExp = getConfig().getBoolean("override-vanilla-exp")) + Bukkit.getPluginManager().registerEvents(new VanillaExperienceOverride(), this); + + if (getConfig().getBoolean("hotbar-swapping.enabled")) + try { + Bukkit.getPluginManager().registerEvents(new HotbarSwap(getConfig().getConfigurationSection("hotbar-swapping")), this); + } catch (RuntimeException exception) { + getLogger().log(Level.WARNING, "Could not load hotbar swapping: " + exception.getMessage()); + } + + if (getConfig().getBoolean("prevent-spawner-xp")) + Bukkit.getPluginManager().registerEvents(new NoSpawnerEXP(), this); + + if (getConfig().getBoolean("death-exp-loss.enabled")) + Bukkit.getPluginManager().registerEvents(new DeathExperienceLoss(), this); + + if (getConfig().getBoolean("shift-click-player-profile-check")) + Bukkit.getPluginManager().registerEvents(new PlayerProfileCheck(), this); + + Bukkit.getPluginManager().registerEvents(new WaypointsListener(), this); + Bukkit.getPluginManager().registerEvents(new PlayerListener(), this); + Bukkit.getPluginManager().registerEvents(new GoldPouchesListener(), this); + Bukkit.getPluginManager().registerEvents(new BlockListener(), this); + Bukkit.getPluginManager().registerEvents(new LootableChestsListener(), this); + Bukkit.getPluginManager().registerEvents(new GuildListener(), this); + Bukkit.getPluginManager().registerEvents(new FishingListener(), this); + Bukkit.getPluginManager().registerEvents(new PlayerCollectStats(), this); + Bukkit.getPluginManager().registerEvents(new PlayerPressKeyListener(), this); + // Bukkit.getPluginManager().registerEvents(new ClassTriggers(), this); + + /* + * Initialize player data from all online players. This is very important to do + * that after registering all the professses otherwise the player datas can't + * recognize what profess the player has and professes will be lost + */ + Bukkit.getOnlinePlayers().forEach(player -> dataProvider.getDataManager().setup(player.getUniqueId())); + + // load guild data after loading player data + dataProvider.getGuildManager().load(); + + // Command + try { + final Field bukkitCommandMap = Bukkit.getServer().getClass().getDeclaredField("commandMap"); + + bukkitCommandMap.setAccessible(true); + CommandMap commandMap = (CommandMap) bukkitCommandMap.get(Bukkit.getServer()); + + FileConfiguration config = new ConfigFile("commands").getConfig(); + + if (config.contains("player")) + commandMap.register("mmocore", new PlayerStatsCommand(config.getConfigurationSection("player"))); + if (config.contains("attributes")) + commandMap.register("mmocore", new AttributesCommand(config.getConfigurationSection("attributes"))); + if (config.contains("class")) + commandMap.register("mmocore", new ClassCommand(config.getConfigurationSection("class"))); + if (config.contains("waypoints")) + commandMap.register("mmocore", new WaypointsCommand(config.getConfigurationSection("waypoints"))); + if (config.contains("quests")) + commandMap.register("mmocore", new QuestsCommand(config.getConfigurationSection("quests"))); + if (config.contains("skills")) + commandMap.register("mmocore", new SkillsCommand(config.getConfigurationSection("skills"))); + if (config.contains("friends")) + commandMap.register("mmocore", new FriendsCommand(config.getConfigurationSection("friends"))); + if (config.contains("party")) + commandMap.register("mmocore", new PartyCommand(config.getConfigurationSection("party"))); + if (config.contains("guild")) + commandMap.register("mmocore", new GuildCommand(config.getConfigurationSection("guild"))); + if (config.contains("skill-tree")) + commandMap.register("mmocore", new SkillTreeCommand(config.getConfigurationSection("skill-tree"))); + if (hasEconomy() && economy.isValid()) { + if (config.contains("withdraw")) + commandMap.register("mmocore", new WithdrawCommand(config.getConfigurationSection("withdraw"))); + if (config.contains("deposit")) + commandMap.register("mmocore", new DepositCommand(config.getConfigurationSection("deposit"))); + } + } catch (NoSuchFieldException | IllegalArgumentException | IllegalAccessException ex) { + ex.printStackTrace(); + } + + MMOCoreCommandTreeRoot mmoCoreCommand = new MMOCoreCommandTreeRoot(); + getCommand("mmocore").setExecutor(mmoCoreCommand); + getCommand("mmocore").setTabCompleter(mmoCoreCommand); + + if (getConfig().getBoolean("auto-save.enabled")) { + int autosave = getConfig().getInt("auto-save.interval") * 20; + new BukkitRunnable() { + public void run() { + + // Save player data + for (PlayerData data : PlayerData.getAll()) + if (data.isFullyLoaded()) + dataProvider.getDataManager().saveData(data); + + // Save guild info + for (Guild guild : dataProvider.getGuildManager().getAll()) + dataProvider.getGuildManager().save(guild); + } + }.runTaskTimerAsynchronously(MMOCore.plugin, autosave, autosave); + } + } + + public void disable() { + + // Save player data + for (PlayerData data : PlayerData.getAll()) + if (data.isFullyLoaded()) { + data.close(); + dataProvider.getDataManager().saveData(data); + } + + // Save guild info + for (Guild guild : dataProvider.getGuildManager().getAll()) + dataProvider.getGuildManager().save(guild); + + // Close MySQL data provider (memory leaks) + if (dataProvider instanceof MySQLDataProvider) + ((MySQLDataProvider) dataProvider).close(); + + // Reset active blocks + mineManager.resetRemainingBlocks(); + + // Clear spawned loot chests + lootChests.getActive().forEach(chest -> chest.expire(false)); + } + + /** + * Called either when the server starts when initializing the manager for + * the first time, or when issuing a plugin reload; in that case, stuff + * like listeners must all be cleared before. + *

+ * Also see {@link MMOCoreManager} + * + * @param clearBefore True when issuing a plugin reload + */ + public void initializePlugin(boolean clearBefore) { + if (clearBefore) + reloadConfig(); + + configManager = new ConfigManager(); + + if (clearBefore) + MythicLib.plugin.getSkills().initialize(true); + skillManager.initialize(clearBefore); + mineManager.initialize(clearBefore); + partyManager.initialize(clearBefore); + attributeManager.initialize(clearBefore); + + // Experience must be loaded before professions and classes + experience.initialize(clearBefore); + + // Drop tables must be loaded before professions + dropTableManager.initialize(clearBefore); + + professionManager.initialize(clearBefore); + classManager.initialize(clearBefore); + + InventoryManager.load(); + + questManager.initialize(clearBefore); + lootChests.initialize(clearBefore); + restrictionManager.initialize(clearBefore); + waypointManager.initialize(clearBefore); + requestManager.initialize(clearBefore); + soundManager.initialize(clearBefore); + configItems.initialize(clearBefore); + skillTreeManager.initialize(clearBefore); + + if (getConfig().isConfigurationSection("action-bar")) + actionBarManager.reload(getConfig().getConfigurationSection("action-bar")); + + StatType.load(); + + if (clearBefore) + PlayerData.getAll().forEach(PlayerData::update); + } + + public static void log(String message) { + log(Level.INFO, message); + } + + public static void debug(int value, String message) { + debug(value, Level.INFO, message); + } + + public static void log(Level level, String message) { + plugin.getLogger().log(level, message); + } + + public static void debug(int value, Level level, String message) { + if (DebugMode.level > (value - 1)) plugin.getLogger().log(level, message); + } + + public File getJarFile() { + return getFile(); + } + + public boolean hasEconomy() { + return economy != null && economy.isValid(); + } + + public static void sqlDebug(String s) { + if (!MMOCore.plugin.shouldDebugSQL) return; + MMOCore.plugin.getLogger().warning("- [SQL Debug] " + s); + } } diff --git a/src/main/java/net/Indyuce/mmocore/api/player/PlayerData.java b/src/main/java/net/Indyuce/mmocore/api/player/PlayerData.java index a5ed447a..d4ec1909 100644 --- a/src/main/java/net/Indyuce/mmocore/api/player/PlayerData.java +++ b/src/main/java/net/Indyuce/mmocore/api/player/PlayerData.java @@ -1,6 +1,5 @@ package net.Indyuce.mmocore.api.player; -import io.lumine.mythic.lib.MythicLib; import io.lumine.mythic.lib.api.player.MMOPlayerData; import io.lumine.mythic.lib.player.TemporaryPlayerData; import io.lumine.mythic.lib.player.cooldown.CooldownMap; @@ -8,7 +7,9 @@ import net.Indyuce.mmocore.MMOCore; import net.Indyuce.mmocore.api.ConfigMessage; import net.Indyuce.mmocore.api.SoundEvent; import net.Indyuce.mmocore.player.Unlockable; -import net.Indyuce.mmocore.waypoint.CostType; +import net.Indyuce.mmocore.tree.NodeState; +import net.Indyuce.mmocore.tree.SkillTreeNode; +import net.Indyuce.mmocore.tree.skilltree.SkillTree; import net.Indyuce.mmocore.waypoint.Waypoint; import net.Indyuce.mmocore.api.event.PlayerExperienceGainEvent; import net.Indyuce.mmocore.api.event.PlayerLevelUpEvent; @@ -49,10 +50,12 @@ import org.bukkit.potion.PotionEffect; import org.bukkit.potion.PotionEffectType; import org.bukkit.scheduler.BukkitRunnable; import org.jetbrains.annotations.NotNull; +import org.w3c.dom.Node; import javax.annotation.Nullable; import java.util.*; import java.util.logging.Level; +import java.util.stream.Collectors; public class PlayerData extends OfflinePlayerData implements Closable, ExperienceTableClaimer { @@ -113,6 +116,10 @@ public class PlayerData extends OfflinePlayerData implements Closable, Experienc */ private boolean fullyLoaded = false; + + //Value of the last skill tree the player was viewing + private SkillTree cachedSkillTree = null; + private final HashMap nodeStates= new HashMap<>(); /** * If the player data was loaded using temporary data. * See {@link TemporaryPlayerData} for more info @@ -168,6 +175,14 @@ public class PlayerData extends OfflinePlayerData implements Closable, Experienc } } + public NodeState getNodeState(SkillTreeNode node) { + return nodeStates.get(node); + } + + public void setNodeState(SkillTreeNode node,NodeState nodeState) { + nodeStates.put(node,nodeState); + } + @Override public void close() { @@ -226,6 +241,17 @@ public class PlayerData extends OfflinePlayerData implements Closable, Experienc return Math.max(1, level); } + public void setCachedSkillTree(SkillTree cachedSkillTree) { + this.cachedSkillTree = cachedSkillTree; + } + + public SkillTree getCachedSkillTree() { + + if (cachedSkillTree == null) + return MMOCore.plugin.skillTreeManager.getAll().stream().collect(Collectors.toList()).get(0); + return cachedSkillTree; + } + @Nullable public AbstractParty getParty() { return MMOCore.plugin.partyModule.getParty(this); @@ -548,7 +574,7 @@ public class PlayerData extends OfflinePlayerData implements Closable, Experienc member.giveExperience(value, EXPSource.PARTY_SHARING, null, false); } - PlayerExperienceGainEvent event = new PlayerExperienceGainEvent(this, value, source); + PlayerExperienceGainEvent event = new PlayerExperienceGainEvent(this, value, source); Bukkit.getPluginManager().callEvent(event); if (event.isCancelled()) return; diff --git a/src/main/java/net/Indyuce/mmocore/api/util/MMOCoreUtils.java b/src/main/java/net/Indyuce/mmocore/api/util/MMOCoreUtils.java index b15bb60a..8a94b8c8 100644 --- a/src/main/java/net/Indyuce/mmocore/api/util/MMOCoreUtils.java +++ b/src/main/java/net/Indyuce/mmocore/api/util/MMOCoreUtils.java @@ -29,6 +29,7 @@ import java.io.ByteArrayOutputStream; import java.util.ArrayList; import java.util.Arrays; import java.util.List; +import java.util.Locale; public class MMOCoreUtils { public static boolean pluginItem(ItemStack item) { @@ -54,6 +55,10 @@ public class MMOCoreUtils { return builder.toString(); } + public static String toEnumName(String str) { + return str.replace("-", "_").toUpperCase(); + } + /** * Displays an in game indicator using a hologram. This uses * LumineUtils hologramFactory to summon holograms diff --git a/src/main/java/net/Indyuce/mmocore/command/SkillTreeCommand.java b/src/main/java/net/Indyuce/mmocore/command/SkillTreeCommand.java new file mode 100644 index 00000000..77e67f4a --- /dev/null +++ b/src/main/java/net/Indyuce/mmocore/command/SkillTreeCommand.java @@ -0,0 +1,38 @@ +package net.Indyuce.mmocore.command; + +import net.Indyuce.mmocore.api.event.MMOCommandEvent; +import net.Indyuce.mmocore.api.player.PlayerData; +import net.Indyuce.mmocore.manager.InventoryManager; +import org.bukkit.Bukkit; +import org.bukkit.command.Command; +import org.bukkit.command.CommandExecutor; +import org.bukkit.command.CommandSender; +import org.bukkit.command.defaults.BukkitCommand; +import org.bukkit.configuration.ConfigurationSection; +import org.bukkit.entity.Player; +import org.jetbrains.annotations.NotNull; + +import java.util.Optional; + +public class SkillTreeCommand extends BukkitCommand { + public SkillTreeCommand(ConfigurationSection config) { + super(config.getString("main")); + + setAliases(config.getStringList("aliases")); + setDescription("Opens the skills menu."); + } + @Override + public boolean execute(@NotNull CommandSender sender, String s, String[] args) { + if (!(sender instanceof Player)) + return false; + PlayerData data = PlayerData.get((Player) sender); + MMOCommandEvent event = new MMOCommandEvent(data, "skills"); + Bukkit.getServer().getPluginManager().callEvent(event); + if (event.isCancelled()) + return true; + InventoryManager.TREE_VIEW.newInventory(data).open(); + return true; + } + + +} diff --git a/src/main/java/net/Indyuce/mmocore/gui/SkillTreeViewer.java b/src/main/java/net/Indyuce/mmocore/gui/SkillTreeViewer.java new file mode 100644 index 00000000..955d7b85 --- /dev/null +++ b/src/main/java/net/Indyuce/mmocore/gui/SkillTreeViewer.java @@ -0,0 +1,345 @@ +package net.Indyuce.mmocore.gui; + +import io.lumine.mythic.lib.player.modifier.PlayerModifier; +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.item.InventoryItem; +import net.Indyuce.mmocore.gui.api.item.Placeholders; +import net.Indyuce.mmocore.gui.api.item.SimplePlaceholderItem; +import net.Indyuce.mmocore.tree.IntegerCoordinates; +import net.Indyuce.mmocore.tree.NodeState; +import net.Indyuce.mmocore.tree.skilltree.SkillTree; +import net.Indyuce.mmocore.tree.SkillTreeNode; +import org.apache.commons.lang.Validate; +import org.bukkit.Material; +import org.bukkit.NamespacedKey; +import org.bukkit.configuration.ConfigurationSection; +import org.bukkit.event.inventory.InventoryAction; +import org.bukkit.event.inventory.InventoryClickEvent; +import org.bukkit.inventory.ItemStack; +import org.bukkit.inventory.meta.ItemMeta; +import org.bukkit.persistence.PersistentDataContainer; +import org.bukkit.persistence.PersistentDataType; + +import java.util.List; + +public class SkillTreeViewer extends EditableInventory { + + + public SkillTreeViewer() { + super("skill-tree"); + } + + @Override + public InventoryItem load(String function, ConfigurationSection config) { + if (function.equals("skill-tree")) { + return new SkillTreeItem(config); + } + if (function.equals("skill-tree-node")) + return new SkillTreeNodeItem(config); + if (function.equals("next-tree-list-page")) { + return new NextTreeListPageItem(config); + } + if (function.equals("previous-tree-list-page")) { + return new PreviousTreeListPageItem(config); + } + return null; + } + + + public SkillTreeInventory newInventory(PlayerData playerData) { + return new SkillTreeInventory(playerData, this); + } + + + public class SkillTreeItem extends InventoryItem { + + public SkillTreeItem(ConfigurationSection config) { + super(config); + + } + + @Override + public ItemStack display(SkillTreeInventory inv, int n) { + int index = 4 * inv.treeListPage + n; + SkillTree skillTree = MMOCore.plugin.skillTreeManager.get(index); + //We display with the material corresponding to the skillTree + ItemStack item = super.display(inv, n, skillTree.getGuiMaterial()); + ItemMeta meta = item.getItemMeta(); + PersistentDataContainer container = meta.getPersistentDataContainer(); + container.set(new NamespacedKey(MMOCore.plugin, "skill-tree-id"), PersistentDataType.STRING, skillTree.getId()); + item.setItemMeta(meta); + return item; + } + + @Override + public Placeholders getPlaceholders(SkillTreeInventory inv, int n) { + int index = 4 * inv.treeListPage + n; + SkillTree skillTree = MMOCore.plugin.skillTreeManager.get(index); + Placeholders holders = new Placeholders(); + holders.register("name", skillTree.getName()); + holders.register("id", skillTree.getId()); + return holders; + } + } + + public class NextTreeListPageItem extends SimplePlaceholderItem { + + public NextTreeListPageItem(ConfigurationSection config) { + super(config); + } + + @Override + public boolean canDisplay(SkillTreeInventory inv) { + return inv.getTreeListPage() < inv.getMaxTreeListPage(); + } + } + + public class PreviousTreeListPageItem extends SimplePlaceholderItem { + + public PreviousTreeListPageItem(ConfigurationSection config) { + super(config); + } + + @Override + public boolean canDisplay(SkillTreeInventory inv) { + return inv.getTreeListPage() > 0; + } + } + + + public class SkillTreeNodeItem extends InventoryItem { + private final LockedSkillNodeItem lockedSkillNode; + private final UnlockedSkillNodeItem unlockedSkillNode; + private final PathTreeNodeItem pathTreeNode; + + + public SkillTreeNodeItem(ConfigurationSection config) { + super(config); + Validate.isTrue(config.contains("locked-skill-node")); + Validate.isTrue(config.contains("unlocked-skill-node")); + Validate.isTrue(config.contains("path-tree-node")); + lockedSkillNode = new LockedSkillNodeItem(config.getConfigurationSection("locked-skill-node")); + unlockedSkillNode = new UnlockedSkillNodeItem(config.getConfigurationSection("unlocked-skill-node")); + pathTreeNode = new PathTreeNodeItem(config.getConfigurationSection("path-tree-node")); + } + + + @Override + public ItemStack display(SkillTreeInventory inv, int n) { + int slot = getSlots().get(n); + int deltaX = (slot - inv.getMinSlot()) % 9; + int deltaY = (slot - inv.getMinSlot()) / 9; + IntegerCoordinates coordinates = new IntegerCoordinates(inv.getX() + deltaX, inv.getY() + deltaY); + ItemStack item=null; + if (inv.getSkillTree().isNode(coordinates)) { + SkillTreeNode node = inv.getSkillTree().getNode(coordinates); + if (inv.getPlayerData().getNodeState(node).equals(NodeState.UNLOCKED)) + item = unlockedSkillNode.display(inv, n, coordinates); + else if (inv.getPlayerData().getNodeState(node).equals(NodeState.LOCKED)) + item = lockedSkillNode.display(inv, n, coordinates); + + } //We check if is a path only if the skillTree is an automatic Skill Tree + else if (inv.getSkillTree().isPath(coordinates)) + item = pathTreeNode.display(inv, n); + else + //If it is none of the above we just display air + return new ItemStack(Material.AIR); + + //We save the coordinates of the node + ItemMeta meta = item.getItemMeta(); + PersistentDataContainer container = meta.getPersistentDataContainer(); + container.set(new NamespacedKey(MMOCore.plugin, "coordinates.x"), PersistentDataType.INTEGER, coordinates.getX()); + container.set(new NamespacedKey(MMOCore.plugin, "coordinates.y"), PersistentDataType.INTEGER, coordinates.getY()); + item.setItemMeta(meta); + return item; + } + + + @Override + public Placeholders getPlaceholders(SkillTreeInventory inv, int n) { + return new Placeholders(); + } + + } + + public class LockedSkillNodeItem extends InventoryItem { + + public LockedSkillNodeItem(ConfigurationSection config) { + super(config); + } + + public ItemStack display(SkillTreeInventory inv, int n, IntegerCoordinates coordinates) { + return super.display(inv, n); + } + + @Override + public Placeholders getPlaceholders(SkillTreeInventory inv, int n) { + int slot = getSlots().get(n); + int deltaX = (slot - inv.getMinSlot()) % 9; + int deltaY = (slot - inv.getMinSlot()) / 9; + IntegerCoordinates coordinates = new IntegerCoordinates(inv.getX() + deltaX, inv.getY() + deltaY); + SkillTreeNode treeNode = inv.getSkillTree().getNode(coordinates); + Placeholders holders = new Placeholders(); + holders.register("name", treeNode.getName()); + holders.register("node-state", inv.getPlayerData().getNodeState(treeNode)); + //Display what nodes this node unlocks + String str = ""; + for (SkillTreeNode node : treeNode.getChildren()) + str += node.getName() + ","; + //We remove the last comma + str = str.substring(0, str.length() - 1); + holders.register("unlocks", str); + //Display all the modifiers this node gives + str = ""; + for (PlayerModifier playerModifier : treeNode.getModifiers()) { + //TODO + str += "\n" + playerModifier.getKey(); + } + + holders.register("modifiers", str); + + return holders; + } + } + + public class UnlockedSkillNodeItem extends InventoryItem { + public UnlockedSkillNodeItem(ConfigurationSection config) { + super(config); + } + + + public ItemStack display(SkillTreeInventory inv, int n, IntegerCoordinates coordinates) { + return super.display(inv, n); + } + + @Override + public Placeholders getPlaceholders(SkillTreeInventory inv, int n) { + int slot = getSlots().get(n); + int deltaX = (slot - inv.getMinSlot()) % 9; + int deltaY = (slot - inv.getMinSlot()) / 9; + IntegerCoordinates coordinates = new IntegerCoordinates(inv.getX() + deltaX, inv.getY() + deltaY); + SkillTreeNode treeNode = inv.getSkillTree().getNode(coordinates); + Placeholders holders = new Placeholders(); + holders.register("name", treeNode.getName()); + holders.register("node-state", inv.getPlayerData().getNodeState(treeNode)); + String str = ""; + for (SkillTreeNode node : treeNode.getChildren()) + str += node.getName() + ","; + //We remove the last comma + str = str.substring(0, str.length() - 1); + holders.register("unlocks", str); + str = ""; + for (PlayerModifier playerModifier : treeNode.getModifiers()) { + //TODO + str += "\n" + playerModifier.getKey(); + } + + holders.register("modifiers", str); + return holders; + } + + + } + + public class PathTreeNodeItem extends SimplePlaceholderItem { + public PathTreeNodeItem(ConfigurationSection config) { + super(config); + } + + } + + + public class SkillTreeInventory extends GeneratedInventory { + private int x, y; + //width and height correspond to the the size of the 'board' representing the skill tree + private int minSlot, middleSlot, maxSlot; + private final int width, height; + private int treeListPage; + private final int maxTreeListPage; + private final SkillTree skillTree; + + + public SkillTreeInventory(PlayerData playerData, EditableInventory editable) { + super(playerData, editable); + skillTree = playerData.getCachedSkillTree(); + + maxTreeListPage = MMOCore.plugin.skillTreeManager.getAll().size() / 4; + + //We get the width and height of the GUI(corresponding to the slots given) + List slots = getByFunction("skill-tree-node").getSlots(); + minSlot = 64; + maxSlot = 0; + for (int slot : slots) { + if (slot < minSlot) + minSlot = slot; + if (slot > maxSlot) + maxSlot = slot; + } + width = (maxSlot - minSlot) % 9; + height = (maxSlot - minSlot) / 9; + middleSlot = minSlot + width / 2 + 9 * (height / 2); + } + + public int getX() { + return x; + } + + public int getY() { + return y; + } + + public int getTreeListPage() { + return treeListPage; + } + + public int getMaxTreeListPage() { + return maxTreeListPage; + } + + @Override + public String calculateName() { + return getEditable().getName().replace("{skill-tree-name}", skillTree.getName()).replace("{skill-tree-id}", skillTree.getId()); + } + + public SkillTree getSkillTree() { + return skillTree; + } + + public int getMinSlot() { + return minSlot; + } + + @Override + public void whenClicked(InventoryClickEvent event, InventoryItem item) { + if (event.getAction().equals(InventoryAction.MOVE_TO_OTHER_INVENTORY)) { + int offset = event.getSlot() - middleSlot; + x += offset % 9; + y += offset / 9; + open(); + return; + } + if (item.getFunction().equals("next-tree-list-page")) { + treeListPage++; + open(); + } + + if (item.getFunction().equals("previous-tree-list-page")) { + treeListPage--; + open(); + } + + if (item.getFunction().equals("skill-tree")) { + String id = event.getCurrentItem().getItemMeta().getPersistentDataContainer().get( + new NamespacedKey(MMOCore.plugin, "skill-tree-id"), PersistentDataType.STRING); + playerData.setCachedSkillTree(MMOCore.plugin.skillTreeManager.get(id)); + newInventory(playerData).open(); + } + if (item.getFunction().equals("skill-tree-node")) { + + } + } + } +} diff --git a/src/main/java/net/Indyuce/mmocore/gui/api/item/InventoryItem.java b/src/main/java/net/Indyuce/mmocore/gui/api/item/InventoryItem.java index 1de7b85d..86b737ca 100644 --- a/src/main/java/net/Indyuce/mmocore/gui/api/item/InventoryItem.java +++ b/src/main/java/net/Indyuce/mmocore/gui/api/item/InventoryItem.java @@ -133,10 +133,14 @@ public abstract class InventoryItem { return display(inv, 0); } - public ItemStack display(T inv, int n) { + public ItemStack display(T inv,int n) { + return display(inv,n,null); + } + + public ItemStack display(T inv, int n,Material specificMaterial) { Placeholders placeholders = getPlaceholders(inv, n); - ItemStack item = new ItemStack(material); + ItemStack item = new ItemStack(specificMaterial==null?material:specificMaterial); ItemMeta meta = item.getItemMeta(); if (texture != null && meta instanceof SkullMeta) diff --git a/src/main/java/net/Indyuce/mmocore/manager/InventoryManager.java b/src/main/java/net/Indyuce/mmocore/manager/InventoryManager.java index 46b9a8f6..599de4bf 100644 --- a/src/main/java/net/Indyuce/mmocore/manager/InventoryManager.java +++ b/src/main/java/net/Indyuce/mmocore/manager/InventoryManager.java @@ -6,15 +6,7 @@ import java.util.logging.Level; import net.Indyuce.mmocore.MMOCore; import net.Indyuce.mmocore.api.ConfigFile; -import net.Indyuce.mmocore.gui.AttributeView; -import net.Indyuce.mmocore.gui.ClassConfirmation; -import net.Indyuce.mmocore.gui.ClassSelect; -import net.Indyuce.mmocore.gui.PlayerStats; -import net.Indyuce.mmocore.gui.QuestViewer; -import net.Indyuce.mmocore.gui.SkillList; -import net.Indyuce.mmocore.gui.SubclassConfirmation; -import net.Indyuce.mmocore.gui.SubclassSelect; -import net.Indyuce.mmocore.gui.WaypointViewer; +import net.Indyuce.mmocore.gui.*; import net.Indyuce.mmocore.gui.api.EditableInventory; import net.Indyuce.mmocore.gui.social.friend.EditableFriendList; import net.Indyuce.mmocore.gui.social.friend.EditableFriendRemoval; @@ -24,32 +16,32 @@ import net.Indyuce.mmocore.gui.social.party.EditablePartyCreation; import net.Indyuce.mmocore.gui.social.party.EditablePartyView; public class InventoryManager { - public static final PlayerStats PLAYER_STATS = new PlayerStats(); - 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 WaypointViewer WAYPOINTS = new WaypointViewer(); - public static final EditableFriendList FRIEND_LIST = new EditableFriendList(); - public static final EditableFriendRemoval FRIEND_REMOVAL = new EditableFriendRemoval(); - public static final EditablePartyView PARTY_VIEW = new EditablePartyView(); - public static final EditablePartyCreation PARTY_CREATION = new EditablePartyCreation(); - public static final EditableGuildView GUILD_VIEW = new EditableGuildView(); - public static final EditableGuildCreation GUILD_CREATION = new EditableGuildCreation(); - public static final QuestViewer QUEST_LIST = new QuestViewer(); - public static final AttributeView ATTRIBUTE_VIEW = new AttributeView(); + public static final PlayerStats PLAYER_STATS = new PlayerStats(); + 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 WaypointViewer WAYPOINTS = new WaypointViewer(); + public static final EditableFriendList FRIEND_LIST = new EditableFriendList(); + public static final EditableFriendRemoval FRIEND_REMOVAL = new EditableFriendRemoval(); + public static final EditablePartyView PARTY_VIEW = new EditablePartyView(); + public static final EditablePartyCreation PARTY_CREATION = new EditablePartyCreation(); + public static final EditableGuildView GUILD_VIEW = new EditableGuildView(); + public static final EditableGuildCreation GUILD_CREATION = new EditableGuildCreation(); + 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 list = Arrays.asList(PLAYER_STATS, ATTRIBUTE_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 list = Arrays.asList(PLAYER_STATS, ATTRIBUTE_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 void load() { - list.forEach(inv -> { - MMOCore.plugin.configManager.loadDefaultFile("gui", inv.getId() + ".yml"); - try { - inv.reload(new ConfigFile("/gui", inv.getId()).getConfig()); - } catch (IllegalArgumentException exception) { - MMOCore.log(Level.WARNING, "Could not load inventory " + inv.getId() + ": " + exception.getMessage()); - } - }); - } + public static void load() { + list.forEach(inv -> { + MMOCore.plugin.configManager.loadDefaultFile("gui", inv.getId() + ".yml"); + try { + inv.reload(new ConfigFile("/gui", inv.getId()).getConfig()); + } catch (IllegalArgumentException exception) { + MMOCore.log(Level.WARNING, "Could not load inventory " + inv.getId() + ": " + exception.getMessage()); + } + }); + } } diff --git a/src/main/java/net/Indyuce/mmocore/manager/SkillTreeManager.java b/src/main/java/net/Indyuce/mmocore/manager/SkillTreeManager.java index 29a5b7bd..1d005cf9 100644 --- a/src/main/java/net/Indyuce/mmocore/manager/SkillTreeManager.java +++ b/src/main/java/net/Indyuce/mmocore/manager/SkillTreeManager.java @@ -1,21 +1,63 @@ package net.Indyuce.mmocore.manager; +import net.Indyuce.mmocore.MMOCore; +import net.Indyuce.mmocore.gui.SkillTreeViewer; import net.Indyuce.mmocore.manager.registry.MMOCoreRegister; -import net.Indyuce.mmocore.tree.SkillTree; +import net.Indyuce.mmocore.tree.SkillTreeNode; +import net.Indyuce.mmocore.tree.skilltree.SkillTree; +import org.bukkit.configuration.file.YamlConfiguration; + +import java.io.File; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.List; +import java.util.stream.Collectors; + -@Deprecated public class SkillTreeManager extends MMOCoreRegister { + private final ArrayList skillTreeNodes = new ArrayList<>(); @Override + public void register(SkillTree tree){ + super.register(tree); + tree.getNodes().forEach((node)->skillTreeNodes.add(node)); + } + + + public ArrayList getAllNodes() { + return skillTreeNodes; + } + + public SkillTree get(int index) { + return registered.values().stream().collect(Collectors.toList()).get(index); + } + + @Override + public String getRegisteredObjectName() { return "skill tree"; } + @Override public void initialize(boolean clearBefore) { if (clearBefore) registered.clear(); + File file = new File(MMOCore.plugin.getDataFolder() + "/skillTree"); + if (!file.exists()) + file.mkdirs(); + load(file); + } - // TODO + + public void load(File file) { + if (file.isDirectory()) { + List fileList = Arrays.asList(file.listFiles()).stream().sorted().collect(Collectors.toList()); + for (File child : fileList) { + load(child); + } + } else { + register(SkillTree.loadSkillTree(YamlConfiguration.loadConfiguration(file))); + } } } diff --git a/src/main/java/net/Indyuce/mmocore/manager/data/yaml/YAMLPlayerDataManager.java b/src/main/java/net/Indyuce/mmocore/manager/data/yaml/YAMLPlayerDataManager.java index 7efe48fa..fbfd93e5 100644 --- a/src/main/java/net/Indyuce/mmocore/manager/data/yaml/YAMLPlayerDataManager.java +++ b/src/main/java/net/Indyuce/mmocore/manager/data/yaml/YAMLPlayerDataManager.java @@ -10,6 +10,8 @@ import net.Indyuce.mmocore.api.player.stats.StatType; import net.Indyuce.mmocore.guild.provided.Guild; import net.Indyuce.mmocore.manager.data.DataProvider; import net.Indyuce.mmocore.manager.data.PlayerDataManager; +import net.Indyuce.mmocore.tree.NodeState; +import net.Indyuce.mmocore.tree.SkillTreeNode; import org.apache.commons.lang.Validate; import org.bukkit.configuration.file.FileConfiguration; import org.jetbrains.annotations.NotNull; @@ -21,128 +23,137 @@ import java.util.logging.Level; import java.util.stream.Collectors; public class YAMLPlayerDataManager extends PlayerDataManager { - private final DataProvider provider; + private final DataProvider provider; - public YAMLPlayerDataManager(DataProvider provider) { - this.provider = provider; - } + public YAMLPlayerDataManager(DataProvider provider) { + this.provider = provider; + } - @Override - public void loadData(PlayerData data) { - FileConfiguration config = new ConfigFile(data.getUniqueId()).getConfig(); + @Override + public void loadData(PlayerData data) { + FileConfiguration config = new ConfigFile(data.getUniqueId()).getConfig(); - data.setClassPoints(config.getInt("class-points", getDefaultData().getClassPoints())); - data.setSkillPoints(config.getInt("skill-points", getDefaultData().getSkillPoints())); - data.setAttributePoints(config.getInt("attribute-points", getDefaultData().getAttributePoints())); - data.setAttributeReallocationPoints(config.getInt("attribute-realloc-points", getDefaultData().getAttrReallocPoints())); - data.setLevel(config.getInt("level", getDefaultData().getLevel())); - data.setExperience(config.getInt("experience")); - if (config.contains("class")) - data.setClass(MMOCore.plugin.classManager.get(config.getString("class"))); + data.setClassPoints(config.getInt("class-points", getDefaultData().getClassPoints())); + data.setSkillPoints(config.getInt("skill-points", getDefaultData().getSkillPoints())); + data.setAttributePoints(config.getInt("attribute-points", getDefaultData().getAttributePoints())); + data.setAttributeReallocationPoints(config.getInt("attribute-realloc-points", getDefaultData().getAttrReallocPoints())); + data.setLevel(config.getInt("level", getDefaultData().getLevel())); + data.setExperience(config.getInt("experience")); + if (config.contains("class")) + data.setClass(MMOCore.plugin.classManager.get(config.getString("class"))); - if (!data.hasUsedTemporaryData()) { - data.setMana(data.getStats().getStat(StatType.MAX_MANA)); - data.setStamina(data.getStats().getStat(StatType.MAX_STAMINA)); - data.setStellium(data.getStats().getStat(StatType.MAX_STELLIUM)); - } + if (!data.hasUsedTemporaryData()) { + data.setMana(data.getStats().getStat(StatType.MAX_MANA)); + data.setStamina(data.getStats().getStat(StatType.MAX_STAMINA)); + data.setStellium(data.getStats().getStat(StatType.MAX_STELLIUM)); + } - if (config.contains("guild")) { - Guild guild = provider.getGuildManager().getGuild(config.getString("guild")); - data.setGuild(guild.getMembers().has(data.getUniqueId()) ? guild : null); - } - if (config.contains("attribute")) - data.getAttributes().load(config.getConfigurationSection("attribute")); - if (config.contains("profession")) - data.getCollectionSkills().load(config.getConfigurationSection("profession")); - if (config.contains("quest")) - data.getQuestData().load(config.getConfigurationSection("quest")); - data.getQuestData().updateBossBar(); - if (config.contains("waypoints")) - data.getWaypoints().addAll(config.getStringList("waypoints")); - if (config.contains("friends")) - config.getStringList("friends").forEach(str -> data.getFriends().add(UUID.fromString(str))); - if (config.contains("skill")) - config.getConfigurationSection("skill").getKeys(false).forEach(id -> data.setSkillLevel(id, config.getInt("skill." + id))); - if (config.contains("bound-skills")) - for (String id : config.getStringList("bound-skills")) - if (data.getProfess().hasSkill(id)) - data.getBoundSkills().add(data.getProfess().getSkill(id)); + if (config.contains("guild")) { + Guild guild = provider.getGuildManager().getGuild(config.getString("guild")); + data.setGuild(guild.getMembers().has(data.getUniqueId()) ? guild : null); + } + if (config.contains("attribute")) + data.getAttributes().load(config.getConfigurationSection("attribute")); + if (config.contains("profession")) + data.getCollectionSkills().load(config.getConfigurationSection("profession")); + if (config.contains("quest")) + data.getQuestData().load(config.getConfigurationSection("quest")); + data.getQuestData().updateBossBar(); + if (config.contains("waypoints")) + data.getWaypoints().addAll(config.getStringList("waypoints")); + if (config.contains("friends")) + config.getStringList("friends").forEach(str -> data.getFriends().add(UUID.fromString(str))); + if (config.contains("skill")) + config.getConfigurationSection("skill").getKeys(false).forEach(id -> data.setSkillLevel(id, config.getInt("skill." + id))); + if (config.contains("bound-skills")) + for (String id : config.getStringList("bound-skills")) + if (data.getProfess().hasSkill(id)) + data.getBoundSkills().add(data.getProfess().getSkill(id)); + if (config.contains("times-claimed")) + for (String key : config.getConfigurationSection("times-claimed").getKeys(true)) + data.getItemClaims().put(key, config.getInt("times-claimed." + key)); + for (SkillTreeNode node : MMOCore.plugin.skillTreeManager.getAllNodes()) { + String str = config.getString("skill-tree-nodes." + node.getTree().getId() + "." + node.getId()); + if (str == null) + data.setNodeState(node, NodeState.LOCKED); + else + data.setNodeState(node,NodeState.valueOf(str)); + } + // Load class slots, use try so the player can log in. + if (config.contains("class-info")) + for (String key : config.getConfigurationSection("class-info").getKeys(false)) + try { + PlayerClass profess = MMOCore.plugin.classManager.get(key); + Validate.notNull(profess, "Could not find class '" + key + "'"); + data.applyClassInfo(profess, new SavedClassInformation(config.getConfigurationSection("class-info." + key))); + } catch (IllegalArgumentException exception) { + MMOCore.log(Level.WARNING, "Could not load class info '" + key + "': " + exception.getMessage()); + } - if (config.contains("times-claimed")) - for (String key : config.getConfigurationSection("times-claimed").getKeys(true)) - data.getItemClaims().put(key, config.getInt("times-claimed." + key)); + data.setFullyLoaded(); + } - // Load class slots, use try so the player can log in. - if (config.contains("class-info")) - for (String key : config.getConfigurationSection("class-info").getKeys(false)) - try { - PlayerClass profess = MMOCore.plugin.classManager.get(key); - Validate.notNull(profess, "Could not find class '" + key + "'"); - data.applyClassInfo(profess, new SavedClassInformation(config.getConfigurationSection("class-info." + key))); - } catch (IllegalArgumentException exception) { - MMOCore.log(Level.WARNING, "Could not load class info '" + key + "': " + exception.getMessage()); - } + @Override + public void saveData(PlayerData data) { + ConfigFile file = new ConfigFile(data.getUniqueId()); + FileConfiguration config = file.getConfig(); - data.setFullyLoaded(); - } + config.set("class-points", data.getClassPoints()); + config.set("skill-points", data.getSkillPoints()); + config.set("attribute-points", data.getAttributePoints()); + // config.set("skill-realloc-points", skillReallocationPoints); + config.set("attribute-realloc-points", data.getAttributeReallocationPoints()); + config.set("level", data.getLevel()); + config.set("experience", data.getExperience()); + config.set("class", data.getProfess().getId()); + config.set("waypoints", new ArrayList<>(data.getWaypoints())); + config.set("friends", data.getFriends().stream().map(UUID::toString).collect(Collectors.toList())); + config.set("last-login", data.getLastLogin()); + config.set("guild", data.hasGuild() ? data.getGuild().getId() : null); - @Override - public void saveData(PlayerData data) { - ConfigFile file = new ConfigFile(data.getUniqueId()); - FileConfiguration config = file.getConfig(); + config.set("skill", null); + data.mapSkillLevels().forEach((key1, value) -> config.set("skill." + key1, value)); + data.getItemClaims().forEach((key, times) -> config.set("times-claimed." + key, times)); - config.set("class-points", data.getClassPoints()); - config.set("skill-points", data.getSkillPoints()); - config.set("attribute-points", data.getAttributePoints()); - // config.set("skill-realloc-points", skillReallocationPoints); - config.set("attribute-realloc-points", data.getAttributeReallocationPoints()); - config.set("level", data.getLevel()); - config.set("experience", data.getExperience()); - config.set("class", data.getProfess().getId()); - config.set("waypoints", new ArrayList<>(data.getWaypoints())); - config.set("friends", data.getFriends().stream().map(UUID::toString).collect(Collectors.toList())); - config.set("last-login", data.getLastLogin()); - config.set("guild", data.hasGuild() ? data.getGuild().getId() : null); + //Save the node states for the player + for (SkillTreeNode node : MMOCore.plugin.skillTreeManager.getAllNodes()) { + config.set("skill-tree-nodes." + node.getTree().getId() + "." + node.getId(),data.getNodeState(node)); + } + List boundSkills = new ArrayList<>(); + data.getBoundSkills().forEach(skill -> boundSkills.add(skill.getSkill().getHandler().getId())); + config.set("bound-skills", boundSkills); - config.set("skill", null); - data.mapSkillLevels().forEach((key1, value) -> config.set("skill." + key1, value)); - data.getItemClaims().forEach((key, times) -> config.set("times-claimed." + key, times)); + config.set("attribute", null); + config.createSection("attribute"); + data.getAttributes().save(config.getConfigurationSection("attribute")); - List boundSkills = new ArrayList<>(); - data.getBoundSkills().forEach(skill -> boundSkills.add(skill.getSkill().getHandler().getId())); - config.set("bound-skills", boundSkills); + config.set("profession", null); + config.createSection("profession"); + data.getCollectionSkills().save(config.getConfigurationSection("profession")); - config.set("attribute", null); - config.createSection("attribute"); - data.getAttributes().save(config.getConfigurationSection("attribute")); + config.set("quest", null); + config.createSection("quest"); + data.getQuestData().save(config.getConfigurationSection("quest")); - config.set("profession", null); - config.createSection("profession"); - data.getCollectionSkills().save(config.getConfigurationSection("profession")); + config.set("class-info", null); + for (String key : data.getSavedClasses()) { + SavedClassInformation info = data.getClassInfo(key); + config.set("class-info." + key + ".level", info.getLevel()); + config.set("class-info." + key + ".experience", info.getExperience()); + config.set("class-info." + key + ".skill-points", info.getSkillPoints()); + config.set("class-info." + key + ".attribute-points", info.getAttributePoints()); + config.set("class-info." + key + ".attribute-realloc-points", info.getAttributeReallocationPoints()); + info.getSkillKeys().forEach(skill -> config.set("class-info." + key + ".skill." + skill, info.getSkillLevel(skill))); + info.getAttributeKeys() + .forEach(attribute -> config.set("class-info." + key + ".attribute." + attribute, info.getAttributeLevel(attribute))); + } - config.set("quest", null); - config.createSection("quest"); - data.getQuestData().save(config.getConfigurationSection("quest")); + file.save(); + } - config.set("class-info", null); - for (String key : data.getSavedClasses()) { - SavedClassInformation info = data.getClassInfo(key); - config.set("class-info." + key + ".level", info.getLevel()); - config.set("class-info." + key + ".experience", info.getExperience()); - config.set("class-info." + key + ".skill-points", info.getSkillPoints()); - config.set("class-info." + key + ".attribute-points", info.getAttributePoints()); - config.set("class-info." + key + ".attribute-realloc-points", info.getAttributeReallocationPoints()); - info.getSkillKeys().forEach(skill -> config.set("class-info." + key + ".skill." + skill, info.getSkillLevel(skill))); - info.getAttributeKeys() - .forEach(attribute -> config.set("class-info." + key + ".attribute." + attribute, info.getAttributeLevel(attribute))); - } - - file.save(); - } - - @NotNull - @Override - public OfflinePlayerData getOffline(UUID uuid) { - return isLoaded(uuid) ? get(uuid) : new YAMLOfflinePlayerData(uuid); - } + @NotNull + @Override + public OfflinePlayerData getOffline(UUID uuid) { + return isLoaded(uuid) ? get(uuid) : new YAMLOfflinePlayerData(uuid); + } } diff --git a/src/main/java/net/Indyuce/mmocore/quest/MMOCoreQuestModule.java b/src/main/java/net/Indyuce/mmocore/quest/MMOCoreQuestModule.java index 1af7fdbc..c50b3c09 100644 --- a/src/main/java/net/Indyuce/mmocore/quest/MMOCoreQuestModule.java +++ b/src/main/java/net/Indyuce/mmocore/quest/MMOCoreQuestModule.java @@ -6,8 +6,9 @@ import net.Indyuce.mmocore.quest.compat.QuestModule; import org.bukkit.entity.Player; public class MMOCoreQuestModule implements QuestModule { + @Override - public AbstractQuest getQuestOrThrow(String id) { + public AbstractQuest getQuest(String id) { Quest quest=MMOCore.plugin.questManager.get(id); if(quest==null) return null; diff --git a/src/main/java/net/Indyuce/mmocore/quest/compat/BeautyQuestModule.java b/src/main/java/net/Indyuce/mmocore/quest/compat/BeautyQuestModule.java index e1e4f720..e984b203 100644 --- a/src/main/java/net/Indyuce/mmocore/quest/compat/BeautyQuestModule.java +++ b/src/main/java/net/Indyuce/mmocore/quest/compat/BeautyQuestModule.java @@ -8,11 +8,13 @@ import fr.skytasul.quests.structure.Quest; import net.Indyuce.mmocore.quest.AbstractQuest; import org.bukkit.entity.Player; +import javax.annotation.Nonnull; + public class BeautyQuestModule implements QuestModule { @Override - public BeautyQuestQuest getQuestOrThrow(String questId) { + public BeautyQuestQuest getQuest(String questId) { Quest quest=QuestsAPI.getQuests().getQuest(Integer.parseInt(questId)); return quest==null?null:new BeautyQuestQuest(quest); } @@ -20,15 +22,15 @@ public class BeautyQuestModule implements QuestModule{ @Override - public QuestCreatorQuest getQuestOrThrow(String id) { + public QuestCreatorQuest getQuest(String id) { return new QuestCreatorQuest(id); } @Override public boolean hasCompletedQuest(String questId, Player player) { UserQC playerData=UserQC.cachedOrNull(player); - Validate.notNull(playerData,"QuestCreator User hasn't been loaded!"); + if(playerData==null) + return false; //Gets all the quests the player has succeeded at List elements=playerData.getQuestHistory().getElements(questId, Arrays.asList(QuestEndType.SUCCESS),0); for(QuestHistoryElement el:elements) { diff --git a/src/main/java/net/Indyuce/mmocore/quest/compat/QuestModule.java b/src/main/java/net/Indyuce/mmocore/quest/compat/QuestModule.java index d4cae6b4..bb66ecfc 100644 --- a/src/main/java/net/Indyuce/mmocore/quest/compat/QuestModule.java +++ b/src/main/java/net/Indyuce/mmocore/quest/compat/QuestModule.java @@ -3,12 +3,15 @@ package net.Indyuce.mmocore.quest.compat; import net.Indyuce.mmocore.quest.AbstractQuest; import org.bukkit.entity.Player; +import javax.annotation.Nullable; + public interface QuestModule { /** * @return Quest with given name */ - public T getQuestOrThrow(String id); + @Nullable + public T getQuest(String id); /** * @return If a specific player did a certain quest diff --git a/src/main/java/net/Indyuce/mmocore/tree/NodeState.java b/src/main/java/net/Indyuce/mmocore/tree/NodeState.java new file mode 100644 index 00000000..33ff58b0 --- /dev/null +++ b/src/main/java/net/Indyuce/mmocore/tree/NodeState.java @@ -0,0 +1,5 @@ +package net.Indyuce.mmocore.tree; + +public enum NodeState { + LOCKED,UNLOCKED; +} diff --git a/src/main/java/net/Indyuce/mmocore/tree/SkillTree.java b/src/main/java/net/Indyuce/mmocore/tree/SkillTree.java deleted file mode 100644 index 15282002..00000000 --- a/src/main/java/net/Indyuce/mmocore/tree/SkillTree.java +++ /dev/null @@ -1,79 +0,0 @@ -package net.Indyuce.mmocore.tree; - -import net.Indyuce.mmocore.MMOCore; -import net.Indyuce.mmocore.manager.registry.RegisterObject; -import org.apache.commons.lang.Validate; -import org.bukkit.configuration.ConfigurationSection; -import org.jetbrains.annotations.NotNull; - -import java.util.Collection; -import java.util.HashMap; -import java.util.Map; -import java.util.Objects; -import java.util.logging.Level; - -/** - * A passive skill tree that features nodes, or passive skills. - *

- * The player can explore the passive skill tree using the right GUI - * and unlock nodes by spending passive skill points. Unlocking nodes - * grant permanent player modifiers, including - * - stats - * - active or passive MythicLib skills - * - active or passive MMOCore skills - * - extra attribute pts - * - particle or potion effects - * - * @author jules - * @see {@link SkillTreeNode} - */ -public class SkillTree implements RegisterObject { - private final String id, name; - private final Map nodes = new HashMap<>(); - - public SkillTree(ConfigurationSection config) { - this.id = config.getName(); - this.name = Objects.requireNonNull(config.getString("name"), "Could not find skill tree name"); - Validate.isTrue(config.isConfigurationSection("nodes"), "Could not find tree passive skills"); - for (String xKey : config.getConfigurationSection("nodes").getKeys(false)) - for (String yKey : config.getConfigurationSection("nodes." + xKey).getKeys(false)) - try { - int x = Integer.parseInt(xKey), y = Integer.parseInt(yKey); - SkillTreeNode node = new SkillTreeNode(this, x, y, config.getConfigurationSection("nodes." + xKey + "." + yKey)); - nodes.put(node.getCoordinates(), node); - } catch (RuntimeException exception) { - MMOCore.plugin.getLogger().log(Level.WARNING, "Could not load tree node '" + xKey + "." + yKey + "' for skill tree '" + id + "': " + exception.getMessage()); - } - } - - @Override - public String getId() { - return id; - } - - public String getName() { - return name; - } - - public Collection getNodes() { - return nodes.values(); - } - - @NotNull - public SkillTreeNode getNode(IntegerCoordinates coords) { - return Objects.requireNonNull(nodes.get(coords), "Could not find node in tree '" + id + "' with coordinates '" + coords.toString() + "'"); - } - - @Override - public boolean equals(Object o) { - if (this == o) return true; - if (o == null || getClass() != o.getClass()) return false; - SkillTree skillTree = (SkillTree) o; - return id.equals(skillTree.id); - } - - @Override - public int hashCode() { - return Objects.hash(id); - } -} diff --git a/src/main/java/net/Indyuce/mmocore/tree/SkillTreeNode.java b/src/main/java/net/Indyuce/mmocore/tree/SkillTreeNode.java index 61b396ae..cf3d79f8 100644 --- a/src/main/java/net/Indyuce/mmocore/tree/SkillTreeNode.java +++ b/src/main/java/net/Indyuce/mmocore/tree/SkillTreeNode.java @@ -4,27 +4,53 @@ import io.lumine.mythic.lib.MythicLib; import io.lumine.mythic.lib.player.modifier.PlayerModifier; import io.lumine.mythic.lib.util.configobject.ConfigSectionObject; import net.Indyuce.mmocore.MMOCore; +import net.Indyuce.mmocore.api.util.MMOCoreUtils; import net.Indyuce.mmocore.player.Unlockable; +import net.Indyuce.mmocore.tree.skilltree.AutomaticSkillTree; +import net.Indyuce.mmocore.tree.skilltree.SkillTree; import org.apache.commons.lang.Validate; import org.bukkit.configuration.ConfigurationSection; import org.jetbrains.annotations.NotNull; -import java.util.HashSet; -import java.util.List; -import java.util.Objects; -import java.util.Set; +import java.util.*; public class SkillTreeNode implements Unlockable { private final SkillTree tree; - private final String name; - private final IntegerCoordinates coordinates; + private final String name,id; + private IntegerCoordinates coordinates; private final List lore; private final Set modifiers = new HashSet<>(); + private final ArrayList children = new ArrayList<>(); + private final ArrayList parents=new ArrayList<>(); + + + + public SkillTreeNode(SkillTree tree, ConfigurationSection config) { + + Validate.notNull(config, "Config cannot be null"); + this.id=config.getName(); + this.tree = tree; + name = Objects.requireNonNull(config.getString("name"), "Could not find node name"); + lore = config.getStringList("lore"); + Validate.isTrue(config.contains("node-type"),"Could not find the node type"); + + + //If coordinates are precised adn we are not wiht an automaticTreewe set them up + if((!(tree instanceof AutomaticSkillTree))&&config.contains("coordinates.x")&&config.contains("coordinates.y")) { + coordinates=new IntegerCoordinates(config.getInt("coordinates.x"),config.getInt("coordinates.y")); + } + for (String key : config.getConfigurationSection("modifiers").getKeys(false)) { + PlayerModifier mod = MythicLib.plugin.getModifiers().loadPlayerModifier(new ConfigSectionObject(config.getConfigurationSection(key))); + modifiers.add(mod); + } + } public SkillTreeNode(SkillTree tree, int x, int y, ConfigurationSection config) { Validate.notNull(config, "Config cannot be null"); + this.id=config.getName(); this.tree = tree; name = Objects.requireNonNull(config.getString("name"), "Could not find node name"); + Validate.isTrue(config.contains("node-type"),"Could not find the node type"); coordinates = new IntegerCoordinates(x, y); lore = config.getStringList("lore"); for (String key : config.getConfigurationSection("modifiers").getKeys(false)) { @@ -33,6 +59,44 @@ public class SkillTreeNode implements Unlockable { } } + + /** + * + */ + protected void whenPostLoaded(@NotNull ConfigurationSection config) { + + } + + public SkillTree getTree() { + return tree; + } + + //Used when postLoaded + public void addParent(SkillTreeNode parent) { + parents.add(parent); + } + + public void addChild(SkillTreeNode child) {children.add(child);} + + public void setCoordinates(IntegerCoordinates coordinates) { + this.coordinates = coordinates; + } + + + public ArrayList getParents() { + return parents; + } + + public ArrayList getChildren() { + return children; + } + + + + public String getId() { + return id; + } + public String getName() { return name; } @@ -57,6 +121,8 @@ public class SkillTreeNode implements Unlockable { return "skill_tree:" + tree.getId() + "_" + coordinates.getX() + "_" + coordinates.getY(); } + + @Override public boolean equals(Object o) { if (this == o) return true; @@ -91,4 +157,5 @@ public class SkillTreeNode implements Unlockable { String treeId = treeIdBuilder.toString(); return MMOCore.plugin.skillTreeManager.get(treeId).getNode(coords); } + } diff --git a/src/main/java/net/Indyuce/mmocore/tree/skilltree/AutomaticSkillTree.java b/src/main/java/net/Indyuce/mmocore/tree/skilltree/AutomaticSkillTree.java new file mode 100644 index 00000000..a8336f09 --- /dev/null +++ b/src/main/java/net/Indyuce/mmocore/tree/skilltree/AutomaticSkillTree.java @@ -0,0 +1,167 @@ +package net.Indyuce.mmocore.tree.skilltree; + +import net.Indyuce.mmocore.tree.IntegerCoordinates; +import net.Indyuce.mmocore.tree.SkillTreeNode; +import org.apache.commons.lang.Validate; +import org.bukkit.configuration.ConfigurationSection; + +import java.util.ArrayList; +import java.util.HashMap; + +public class AutomaticSkillTree extends SkillTree { + private SkillTreeNode root; + //Represents all the coordinates that will be displayed as a path (between 2 nodes of the tree) + private final ArrayList pathToParents = new ArrayList<>(); + + //Hash map to store the left and right branches of each node + private final HashMap nodeBranches = new HashMap<>(); + + public AutomaticSkillTree(ConfigurationSection config) { + super(config); + } + + @Override + public void whenPostLoaded(ConfigurationSection config) { + + + //We setup the children and parents for each node. + for (SkillTreeNode node : nodes.values()) { + ConfigurationSection section = config.getConfigurationSection(node.getId()); + for (String child : section.getStringList("children")) { + node.addChild(getNode(child)); + getNode(child).addParent(node); + } + } + + + //We find the root of the tree wich is + for (SkillTreeNode node : nodes.values()) { + if (node.getParents().size() == 0) { + Validate.isTrue(root == null, "Their can't be more than 1 root in the skillTree!"); + root = node; + } + } + //We setup the width of all the nodes recursively + setupTreeWidth(root); + //We recursively setup all the coordinates of the tree nodes + root.setCoordinates(new IntegerCoordinates(0, 0)); + setupCoordinates(root); + + //We get and cache the values of minX,minY,maxX and maxY + minX = nodeBranches.get(root).getLeftBranches(); + minY = 0; + maxX = nodeBranches.get(root).getRightBranches(); + + for (SkillTreeNode node : nodes.values()) { + if (node.getCoordinates().getY() > maxY) + maxY = node.getCoordinates().getY(); + } + + //Eventually we setup the coordinateNodesMap + super.setupCoordinatesNodesMap(); + + } + + /** + * Recursive algorithm to automatically calculate the integercoordinates each node should have to have a good display. + * It also fills the list pathToParents representing all the coordinates corresponding to a path between 2 nodes (for the GUI) + * + * @param node the root + */ + private void setupCoordinates(SkillTreeNode node) { + int childrenSize = node.getChildren().size(); + int x = node.getCoordinates().getX(); + ; + int y = node.getCoordinates().getY(); + ; + int leftOffset = 0; + int rightOffset = 0; + for (int i = 0; i < childrenSize; i++) { + SkillTreeNode child = node.getChildren().get(i); + + if (childrenSize % 2 == 0 && i == 0) { + child.setCoordinates(new IntegerCoordinates(x, y + 2)); + leftOffset += 2 + nodeBranches.get(child).getLeftBranches(); + rightOffset += 2 + nodeBranches.get(child).getRightBranches(); + } else if (i % 2 == 0) { + child.setCoordinates(new IntegerCoordinates(x - leftOffset - 2 - nodeBranches.get(child).getWidth(), y + 2)); + leftOffset += 2 + nodeBranches.get(child).getWidth(); + } else { + child.setCoordinates(new IntegerCoordinates(x + rightOffset + 2 + nodeBranches.get(child).getWidth(), y + 2)); + rightOffset += 2 + nodeBranches.get(child).getWidth(); + } + + //We setup the path to parent variable (Used for the GUI) + int childX = child.getCoordinates().getX(); + int childY = child.getCoordinates().getY(); + + int parentX = node.getParents().get(0).getCoordinates().getX(); + pathToParents.add(new IntegerCoordinates(childX, childY - 1)); + int offset = childX > parentX ? -1 : 1; + while (childX != parentX) { + pathToParents.add(new IntegerCoordinates(childX, childY - 2)); + childX += offset; + } + + + //We setup the coordinates for the associated child + setupCoordinates(child); + } + + } + + public Branches getBranches(SkillTreeNode node) { + return nodeBranches.get(node); + } + + /** + * Recursively sed to setup all the right and left branches of the node to later determine its coordinates for GUI display + */ + public void setupTreeWidth(SkillTreeNode node) { + int childrenSize = node.getChildren().size(); + int leftBranches = 0; + int rightBranches = 0; + for (int i = 0; i < childrenSize; i++) { + SkillTreeNode child = node.getChildren().get(i); + setupTreeWidth(child); + if (childrenSize % 2 == 0 && i == 0) { + + leftBranches += nodeBranches.get(child).getLeftBranches(); + rightBranches += nodeBranches.get(child).getRightBranches(); + } else if (i % 2 == 0) { + leftBranches += nodeBranches.get(child).getWidth() + 2; + } else { + rightBranches += nodeBranches.get(child).getWidth() + 2; + } + + } + } + + @Override + public boolean isPath(IntegerCoordinates coordinates) { + return pathToParents.contains(coordinates); + } + + + private class Branches { + private final int leftBranches, rightBranches; + + public Branches(int leftBranches, int rightBranches) { + this.leftBranches = leftBranches; + this.rightBranches = rightBranches; + } + + public int getLeftBranches() { + return leftBranches; + } + + public int getRightBranches() { + return rightBranches; + } + + public int getWidth() { + return leftBranches + rightBranches; + } + } +} + diff --git a/src/main/java/net/Indyuce/mmocore/tree/skilltree/CustomSkillTree.java b/src/main/java/net/Indyuce/mmocore/tree/skilltree/CustomSkillTree.java new file mode 100644 index 00000000..25beed35 --- /dev/null +++ b/src/main/java/net/Indyuce/mmocore/tree/skilltree/CustomSkillTree.java @@ -0,0 +1,21 @@ +package net.Indyuce.mmocore.tree.skilltree; + +import net.Indyuce.mmocore.tree.IntegerCoordinates; +import org.bukkit.configuration.ConfigurationSection; +import org.jetbrains.annotations.NotNull; + +public class CustomSkillTree extends SkillTree{ + public CustomSkillTree(ConfigurationSection config) { + super(config); + } + + @Override + protected void whenPostLoaded(@NotNull ConfigurationSection configurationSection) { + + } + + @Override + public boolean isPath(IntegerCoordinates coordinates) { + return false; + } +} diff --git a/src/main/java/net/Indyuce/mmocore/tree/skilltree/LinkedSkillTree.java b/src/main/java/net/Indyuce/mmocore/tree/skilltree/LinkedSkillTree.java new file mode 100644 index 00000000..a6221fa7 --- /dev/null +++ b/src/main/java/net/Indyuce/mmocore/tree/skilltree/LinkedSkillTree.java @@ -0,0 +1,64 @@ +package net.Indyuce.mmocore.tree.skilltree; + +import io.netty.handler.codec.http.cookie.CookieDecoder; +import net.Indyuce.mmocore.tree.IntegerCoordinates; +import net.Indyuce.mmocore.tree.SkillTreeNode; +import org.apache.commons.lang.Validate; +import org.bukkit.configuration.ConfigurationSection; +import org.jetbrains.annotations.NotNull; + +import java.util.Arrays; +import java.util.List; + +public class LinkedSkillTree extends SkillTree{ + + + public LinkedSkillTree(ConfigurationSection config) { + super(config); + + //We setup the coordinate map because coordinates are given in the yml for linked skill tree + setupCoordinatesNodesMap(); + } + + + + @Override + protected void whenPostLoaded(@NotNull ConfigurationSection configurationSection) { + + SkillTreeNode root=getNode(new IntegerCoordinates(0,0)); + Validate.notNull(root,"Their must be a node(the root of the tree) at the coordinates (0,0) "); + //We setup all the children and parent relations between the nodes + setupChildren(root); + } + + /** + * There is no paths on a linked skill tree + */ + @Override + public boolean isPath(IntegerCoordinates coordinates) { + return false; + } + + /** + * Recursive algorithm to setup the parents and children of each skillTreeNode + */ + public void setupChildren(SkillTreeNode node) { + int x=node.getCoordinates().getX(); + int y=node.getCoordinates().getY(); + List checkCoordinates= Arrays.asList(new IntegerCoordinates(x+1,y), + new IntegerCoordinates(x-1,y),new IntegerCoordinates(x,y+1),new IntegerCoordinates(x,y-1)); + for(IntegerCoordinates coor:checkCoordinates) { + //We add Parent and child only if the node exists and doesn't have a parent already + + if(isNode(coor)) { + SkillTreeNode child=getNode(coor); + if(child.getParents().size()==0) { + child.addParent(node); + node.addChild(child); + //We call recursively the algorithm + setupChildren(child); + }} + + } + } +} diff --git a/src/main/java/net/Indyuce/mmocore/tree/skilltree/SkillTree.java b/src/main/java/net/Indyuce/mmocore/tree/skilltree/SkillTree.java new file mode 100644 index 00000000..1b15c8d8 --- /dev/null +++ b/src/main/java/net/Indyuce/mmocore/tree/skilltree/SkillTree.java @@ -0,0 +1,154 @@ +package net.Indyuce.mmocore.tree.skilltree; + +import io.lumine.mythic.lib.api.util.PostLoadObject; +import net.Indyuce.mmocore.api.util.MMOCoreUtils; +import net.Indyuce.mmocore.manager.registry.RegisterObject; +import net.Indyuce.mmocore.tree.IntegerCoordinates; +import net.Indyuce.mmocore.tree.NodeState; +import net.Indyuce.mmocore.tree.SkillTreeNode; +import org.apache.commons.lang.Validate; +import org.bukkit.Material; +import org.bukkit.configuration.ConfigurationSection; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; + +import java.util.*; + +/** + * A passive skill tree that features nodes, or passive skills. + *

+ * The player can explore the passive skill tree using the right GUI + * and unlock nodes by spending passive skill points. Unlocking nodes + * grant permanent player modifiers, including + * - stats + * - active or passive MythicLib skills + * - active or passive MMOCore skills + * - extra attribute pts + * - particle or potion effects + * + * @author jules + * @author Ka0rX + * @see {@link SkillTreeNode} + */ +public abstract class SkillTree extends PostLoadObject implements RegisterObject { + private final String id, name; + private final Material guiMaterial; + //2 different maps to get the nodes + protected final Map coordinatesNodes = new HashMap<>(); + protected final Map nodes = new HashMap<>(); + //Caches the height of the skill tree + protected int minX, minY, maxX, maxY; + + public SkillTree(ConfigurationSection config) { + super(config); + this.id = Objects.requireNonNull(config.getString("id"), "Could not find skill tree id"); + this.name = Objects.requireNonNull(config.getString("name"), "Could not find skill tree name"); + this.guiMaterial = Material.valueOf(MMOCoreUtils.toEnumName(Objects.requireNonNull(config.getString("material")))); + Validate.isTrue(config.isConfigurationSection("nodes"), "Could not find any nodes in the tree"); + for (String key : config.getConfigurationSection("nodes").getKeys(false)) { + SkillTreeNode node = new SkillTreeNode(this, config.getConfigurationSection("nodes." + key)); + } + } + + public void setupCoordinatesNodesMap() { + for (SkillTreeNode node : nodes.values()) { + coordinatesNodes.put(node.getCoordinates(), node); + } + } + + @Override + protected abstract void whenPostLoaded(@NotNull ConfigurationSection configurationSection); + + public int getMaxX() { + return maxX; + } + + public int getMinX() { + return minX; + } + + public int getMinY() { + return minY; + } + + public int getMaxY() { + return maxY; + } + + + public static SkillTree loadSkillTree(ConfigurationSection config) { + String string = config.getString("type"); + + Validate.isTrue(string.equals("automatic") || string.equals("linked") || string.equals("custom"), "You must precise the type of the skill tree in the yml!" + + "\nAllowed values: 'automatic','linked','custom'"); + SkillTree skillTree = null; + if (string.equals("automatic")) { + skillTree = new AutomaticSkillTree(config); + skillTree.postLoad(); + } + if (string.equals("linked")) { + skillTree = new LinkedSkillTree(config); + skillTree.postLoad(); + } + if (string.equals("custom")) { + skillTree = new CustomSkillTree(config); + skillTree.postLoad(); + } + + return skillTree; + } + + @Nullable + /** + * Returns null if it is not a node and returns the node type if it a node + */ + public boolean isNode(IntegerCoordinates coordinates) { + for (SkillTreeNode node : nodes.values()) { + if (node.getCoordinates().equals(coordinates)) + return true; + } + return false; + } + + public abstract boolean isPath(IntegerCoordinates coordinates); + + public Material getGuiMaterial() { + return guiMaterial; + } + + @Override + public String getId() { + return id; + } + + public String getName() { + return name; + } + + public Collection getNodes() { + return nodes.values(); + } + + @NotNull + public SkillTreeNode getNode(IntegerCoordinates coords) { + return Objects.requireNonNull(nodes.get(coords), "Could not find node in tree '" + id + "' with coordinates '" + coords.toString() + "'"); + } + + @NotNull + public SkillTreeNode getNode(String name) { + return Objects.requireNonNull(nodes.get(name), "Could not find node in tree '" + id + "' with name '" + name + "'"); + } + + @Override + public boolean equals(Object o) { + if (this == o) return true; + if (o == null || getClass() != o.getClass()) return false; + SkillTree skillTree = (SkillTree) o; + return id.equals(skillTree.id); + } + + @Override + public int hashCode() { + return Objects.hash(id); + } +} diff --git a/src/main/java/net/Indyuce/mmocore/tree/skilltree/SkillTreeType.java b/src/main/java/net/Indyuce/mmocore/tree/skilltree/SkillTreeType.java new file mode 100644 index 00000000..60914414 --- /dev/null +++ b/src/main/java/net/Indyuce/mmocore/tree/skilltree/SkillTreeType.java @@ -0,0 +1,7 @@ +package net.Indyuce.mmocore.tree.skilltree; + +public enum SkillTreeType { + AUTOMATIC_SKILL_TREE(), + LINKED_SKILL_TREE, + CUSTOM_SKILL_TREE; +} diff --git a/src/main/resources/default/commands.yml b/src/main/resources/default/commands.yml index b6ddec29..781e5975 100644 --- a/src/main/resources/default/commands.yml +++ b/src/main/resources/default/commands.yml @@ -28,6 +28,9 @@ guild: withdraw: main: "withdraw" aliases: ["w"] +skill-tree: + main: "skilltree" + aliase: ["st"] deposit: main: "deposit" aliases: ["d"] \ No newline at end of file diff --git a/src/main/resources/default/gui/skill-tree.yml b/src/main/resources/default/gui/skill-tree.yml new file mode 100644 index 00000000..25c3af73 --- /dev/null +++ b/src/main/resources/default/gui/skill-tree.yml @@ -0,0 +1,25 @@ +# GUI display name +name: 'Current Skill Tree: &6{skill-tree-name}' + +# Number of slots in your inventory. Must be +# between 9 and 54 and must be a multiple of 9. +slots: 54 + +items: + skill-tree: + name: '{skill-tree-node}' + function: 'skill-tree' + slots: [9,18,27,36] + + next-tree-list-page: + function: 'next-tree-list-page' + material: 'ARROW' + slots: [ 45 ] + + previous-tree-list-page: + function: 'previous-tree-list-page' + material: "ARROW" + slots: [ 0 ] + + skill-tree-node: + function: 'skill-tree-node' diff --git a/src/main/resources/default/skilltree/combat.yml b/src/main/resources/default/skilltree/combat.yml new file mode 100644 index 00000000..19cceeb5 --- /dev/null +++ b/src/main/resources/default/skilltree/combat.yml @@ -0,0 +1,29 @@ +id: 'combat' +name: 'Combat Skill Tree' + + +#The type of the Skill tree, can be : +#'linked'->You must only precise nodes coordinates. 2 adjacent nodes will be affiliated. The root is at coordinates 0,0. +#'automatic'-> You must only precise the children(there can more than 1) of each node. Each node can only have one parent and the root has none. +# The display coordinates will be automatically calculated to have a good render. +#'custom'-> You must precise coordinates and children for each node, each node can have multiple parents and children. +# The coordinates are only used for display on the GUI but will not have any impact on the affiliation between nodes. + +type: 'linked' + +#The material that will represent the skill tree in the GUI +material: 'DIAMOND_SWORD' + +nodes: + strength: + name: 'Combat strength' + #Coordinates of the node + coordinates: + x: 0 + y: 0 + strength2: + name: 'Combat strength 2' + coordinates: + x: 1 + y: 0 +