Skill Trees in MMOCore

This commit is contained in:
Guillaume 2022-05-24 15:26:22 +02:00
parent a2630824c9
commit fece7347ef
25 changed files with 1590 additions and 656 deletions

View File

@ -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<LootChest> 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<LootChest> 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.
* <p>
* 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);
}
}

View File

@ -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<SkillTreeNode, NodeState> 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;

View File

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

View File

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

View File

@ -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<SkillTreeInventory> {
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<SkillTreeInventory> {
public NextTreeListPageItem(ConfigurationSection config) {
super(config);
}
@Override
public boolean canDisplay(SkillTreeInventory inv) {
return inv.getTreeListPage() < inv.getMaxTreeListPage();
}
}
public class PreviousTreeListPageItem extends SimplePlaceholderItem<SkillTreeInventory> {
public PreviousTreeListPageItem(ConfigurationSection config) {
super(config);
}
@Override
public boolean canDisplay(SkillTreeInventory inv) {
return inv.getTreeListPage() > 0;
}
}
public class SkillTreeNodeItem extends InventoryItem<SkillTreeInventory> {
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<SkillTreeInventory> {
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<SkillTreeInventory> {
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<SkillTreeInventory> {
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<Integer> 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")) {
}
}
}
}

View File

@ -133,10 +133,14 @@ public abstract class InventoryItem<T extends GeneratedInventory> {
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)

View File

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

View File

@ -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<SkillTree> {
private final ArrayList<SkillTreeNode> skillTreeNodes = new ArrayList<>();
@Override
public void register(SkillTree tree){
super.register(tree);
tree.getNodes().forEach((node)->skillTreeNodes.add(node));
}
public ArrayList<SkillTreeNode> 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<File> fileList = Arrays.asList(file.listFiles()).stream().sorted().collect(Collectors.toList());
for (File child : fileList) {
load(child);
}
} else {
register(SkillTree.loadSkillTree(YamlConfiguration.loadConfiguration(file)));
}
}
}

View File

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

View File

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

View File

@ -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<BeautyQuestModule.BeautyQuestQuest> {
@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<BeautyQuestModule.BeautyQu
@Override
public boolean hasCompletedQuest(String questId, Player player) {
PlayerAccount account=PlayersManager.getPlayerAccount(player);
PlayerQuestDatas quest=account.getQuestDatas(QuestsAPI.getQuests().getQuest(Integer.parseInt(questId)));
return quest.isFinished();
Quest quest=QuestsAPI.getQuests().getQuest(Integer.parseInt(questId));
PlayerQuestDatas questData=account.getQuestDatas(quest);
return questData.isFinished();
}
public class BeautyQuestQuest implements AbstractQuest {
Quest quest;
private final Quest quest;
public BeautyQuestQuest(Quest quest) {
this.quest = quest;

View File

@ -14,16 +14,17 @@ public class BlackVeinQuestsModule implements QuestModule<BlackVeinQuestsModule.
@Override
public BlackVeinQuestQuest getQuestOrThrow(String id) {
Quests plugin = (Quests) Bukkit.getPluginManager().getPlugin("Quests");
return plugin.getQuestById(id)==null?null:new BlackVeinQuestQuest(plugin.getQuestById(id));
public BlackVeinQuestQuest getQuest(String id) {
Quest quest=plugin.getQuestById(id);
return quest==null?null:new BlackVeinQuestQuest(quest);
}
@Override
public boolean hasCompletedQuest(String questId, Player player) {
Quester quester = plugin.getQuester(player.getUniqueId());
if(quester==null)
return false;
for(Quest quest:quester.getCompletedQuests()) {
if(quest.getId().equals(questId))
return true;

View File

@ -14,14 +14,15 @@ import java.util.List;
public class QuestCreatorModule implements QuestModule<QuestCreatorModule.QuestCreatorQuest>{
@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<QuestHistoryElement> elements=playerData.getQuestHistory().getElements(questId, Arrays.asList(QuestEndType.SUCCESS),0);
for(QuestHistoryElement el:elements) {

View File

@ -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<T extends AbstractQuest> {
/**
* @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

View File

@ -0,0 +1,5 @@
package net.Indyuce.mmocore.tree;
public enum NodeState {
LOCKED,UNLOCKED;
}

View File

@ -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.
* <p>
* 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<IntegerCoordinates, SkillTreeNode> 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<SkillTreeNode> 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);
}
}

View File

@ -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<String> lore;
private final Set<PlayerModifier> modifiers = new HashSet<>();
private final ArrayList<SkillTreeNode> children = new ArrayList<>();
private final ArrayList<SkillTreeNode> 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<SkillTreeNode> getParents() {
return parents;
}
public ArrayList<SkillTreeNode> 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);
}
}

View File

@ -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<IntegerCoordinates> pathToParents = new ArrayList<>();
//Hash map to store the left and right branches of each node
private final HashMap<SkillTreeNode, Branches> 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;
}
}
}

View File

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

View File

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

View File

@ -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.
* <p>
* 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<IntegerCoordinates, SkillTreeNode> coordinatesNodes = new HashMap<>();
protected final Map<String, SkillTreeNode> 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<SkillTreeNode> 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);
}
}

View File

@ -0,0 +1,7 @@
package net.Indyuce.mmocore.tree.skilltree;
public enum SkillTreeType {
AUTOMATIC_SKILL_TREE(),
LINKED_SKILL_TREE,
CUSTOM_SKILL_TREE;
}

View File

@ -28,6 +28,9 @@ guild:
withdraw:
main: "withdraw"
aliases: ["w"]
skill-tree:
main: "skilltree"
aliase: ["st"]
deposit:
main: "deposit"
aliases: ["d"]

View File

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

View File

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