diff --git a/pom.xml b/pom.xml index 5aedb503..364fc0d2 100644 --- a/pom.xml +++ b/pom.xml @@ -125,7 +125,7 @@ io.lumine MythicLib-dist - 1.3 + 1.3-R17 provided diff --git a/src/main/java/net/Indyuce/mmocore/MMOCore.java b/src/main/java/net/Indyuce/mmocore/MMOCore.java index 8d4b3b8c..d173f3d8 100644 --- a/src/main/java/net/Indyuce/mmocore/MMOCore.java +++ b/src/main/java/net/Indyuce/mmocore/MMOCore.java @@ -1,6 +1,7 @@ package net.Indyuce.mmocore; import io.lumine.mythic.lib.MythicLib; +import io.lumine.mythic.lib.UtilityMethods; import io.lumine.mythic.lib.comp.Metrics; import io.lumine.mythic.lib.version.SpigotPlugin; import io.lumine.mythic.utils.plugin.LuminePlugin; @@ -40,7 +41,7 @@ import net.Indyuce.mmocore.manager.profession.*; import net.Indyuce.mmocore.manager.social.BoosterManager; import net.Indyuce.mmocore.manager.social.PartyManager; import net.Indyuce.mmocore.manager.social.RequestManager; -import net.Indyuce.mmocore.skill.cast.listener.SkillBar; +import net.Indyuce.mmocore.skill.cast.SkillCastingMode; import net.Indyuce.mmocore.skill.list.Ambers; import net.Indyuce.mmocore.skill.list.Neptune_Gift; import net.Indyuce.mmocore.skill.list.Sneaky_Picky; @@ -221,6 +222,14 @@ public class MMOCore extends LuminePlugin { DebugMode.enableActionBar(); } + // 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); diff --git a/src/main/java/net/Indyuce/mmocore/api/SoundEvent.java b/src/main/java/net/Indyuce/mmocore/api/SoundEvent.java new file mode 100644 index 00000000..47993004 --- /dev/null +++ b/src/main/java/net/Indyuce/mmocore/api/SoundEvent.java @@ -0,0 +1,20 @@ +package net.Indyuce.mmocore.api; + +public enum SoundEvent { + LEVEL_UP, + WARP_TELEPORT, + WARP_CANCELLED, + WARP_CHARGE, + WARP_UNLOCK, + HOTBAR_SWAP, + SPELL_CAST_BEGIN, + SPELL_CAST_END, + CANT_SELECT_CLASS, + SELECT_CLASS, + LEVEL_ATTRIBUTE, + RESET_ATTRIBUTES, + NOT_ENOUGH_POINTS, + CANCEL_QUEST, + START_QUEST, + CLOSE_LOOT_CHEST; +} \ No newline at end of file diff --git a/src/main/java/net/Indyuce/mmocore/api/SoundObject.java b/src/main/java/net/Indyuce/mmocore/api/SoundObject.java index 0d1a0ec3..763cc7b6 100644 --- a/src/main/java/net/Indyuce/mmocore/api/SoundObject.java +++ b/src/main/java/net/Indyuce/mmocore/api/SoundObject.java @@ -1,53 +1,106 @@ package net.Indyuce.mmocore.api; +import io.lumine.mythic.lib.UtilityMethods; +import io.lumine.mythic.lib.util.configobject.ConfigObject; +import org.bukkit.Location; import org.bukkit.Sound; +import org.bukkit.configuration.ConfigurationSection; +import org.bukkit.entity.Player; + +import javax.annotation.Nullable; public class SoundObject { - private final Sound sound; - private final String key; - private final float volume; - private final float pitch; - - public SoundObject(String input) { - String[] split = input.split(","); - if(split.length > 2) { - input = split[0]; - volume = Float.parseFloat(split[1]); - pitch = Float.parseFloat(split[2]); - } else { - volume = 1; - pitch = 1; - } - Sound sound = null; - String key = ""; - try { - sound = Sound.valueOf(input.toUpperCase().replace("-", "_")); - } catch(Exception ignored) { - key = input; - } + @Nullable + private final Sound sound; - this.sound = sound; - this.key = key; - } - - public boolean hasSound() { - return key.isEmpty(); - } - - public Sound getSound() { - return sound; - } + @Nullable + private final String key; - public String getKey() { - return key; - } + private final float volume; + private final float pitch; - public float getVolume() { - return volume; - } + public SoundObject(String input) { + String[] split = input.split(","); - public float getPitch() { - return pitch; - } + Sound sound = null; + String key = null; + try { + sound = Sound.valueOf(UtilityMethods.enumName(split[0])); + } catch (Exception ignored) { + key = split[0]; + } + + this.sound = sound; + this.key = key; + + volume = split.length > 1 ? Float.parseFloat(split[1]) : 1; + pitch = split.length > 2 ? Float.parseFloat(split[2]) : 1; + } + + public SoundObject(ConfigurationSection config) { + String input = config.getString("sound"); + + Sound sound = null; + String key = null; + try { + sound = Sound.valueOf(UtilityMethods.enumName(input)); + } catch (Exception ignored) { + key = input; + } + + this.sound = sound; + this.key = key; + + volume = (float) config.getDouble("volume", 1); + pitch = (float) config.getDouble("pitch", 1); + } + + /** + * @return If this object is custom a custom sound, potentially + * from a resource pack + */ + public boolean isCustom() { + return sound == null; + } + + @Nullable + public Sound getSound() { + return sound; + } + + @Nullable + public String getKey() { + return key; + } + + public float getVolume() { + return volume; + } + + public float getPitch() { + return pitch; + } + + public void playTo(Player player) { + playTo(player, volume, pitch); + } + + public void playTo(Player player, float volume, float pitch) { + if (isCustom()) + player.playSound(player.getLocation(), key, volume, pitch); + else + player.playSound(player.getLocation(), sound, volume, pitch); + } + + public void playAt(Location loc) { + playAt(loc, volume, pitch); + } + + public void playAt(Location loc, float volume, float pitch) { + if (isCustom()) + loc.getWorld().playSound(loc, key, volume, pitch); + else + loc.getWorld().playSound(loc, sound, volume, pitch); + } } 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 828f0d69..a10b9048 100644 --- a/src/main/java/net/Indyuce/mmocore/api/player/PlayerData.java +++ b/src/main/java/net/Indyuce/mmocore/api/player/PlayerData.java @@ -5,6 +5,7 @@ import io.lumine.mythic.lib.player.TemporaryPlayerData; import io.lumine.mythic.lib.player.cooldown.CooldownMap; import net.Indyuce.mmocore.MMOCore; import net.Indyuce.mmocore.api.ConfigMessage; +import net.Indyuce.mmocore.api.SoundEvent; import net.Indyuce.mmocore.api.Waypoint; import net.Indyuce.mmocore.api.event.PlayerExperienceGainEvent; import net.Indyuce.mmocore.api.event.PlayerLevelUpEvent; @@ -26,12 +27,12 @@ import net.Indyuce.mmocore.api.util.MMOCoreUtils; import net.Indyuce.mmocore.api.util.math.particle.SmallParticleEffect; import net.Indyuce.mmocore.experience.EXPSource; import net.Indyuce.mmocore.experience.PlayerProfessions; -import net.Indyuce.mmocore.manager.SoundManager; import net.Indyuce.mmocore.skill.ClassSkill; import net.Indyuce.mmocore.skill.RegisteredSkill; -import net.Indyuce.mmocore.skill.cast.listener.SkillBar.SkillCasting; +import net.Indyuce.mmocore.skill.cast.SkillCastingHandler; import net.md_5.bungee.api.ChatMessageType; import net.md_5.bungee.api.chat.TextComponent; +import org.apache.commons.lang.Validate; import org.bukkit.*; import org.bukkit.attribute.Attribute; import org.bukkit.entity.Player; @@ -64,6 +65,7 @@ public class PlayerData extends OfflinePlayerData implements Closable { private double mana, stamina, stellium; private Party party; private Guild guild; + private SkillCastingHandler skillCasting; private final PlayerQuests questData; private final PlayerStats playerStats; @@ -74,11 +76,10 @@ public class PlayerData extends OfflinePlayerData implements Closable { private final PlayerProfessions collectSkills = new PlayerProfessions(this); private final PlayerAttributes attributes = new PlayerAttributes(this); private final Map classSlots = new HashMap<>(); -private final Map lastActivity = new HashMap<>(); + private final Map lastActivity = new HashMap<>(); // NON-FINAL player data stuff made public to facilitate field change public int skillGuiDisplayOffset; - public SkillCasting skillCasting; public boolean noCooldown; public CombatRunnable combat; @@ -438,7 +439,7 @@ private final Map lastActivity = new HashMap<>(); return; if (getPlayer().getLocation().getBlockX() != x || getPlayer().getLocation().getBlockY() != y || getPlayer().getLocation().getBlockZ() != z) { - MMOCore.plugin.soundManager.play(getPlayer(), SoundManager.SoundEvent.WARP_CANCELLED); + MMOCore.plugin.soundManager.getSound(SoundEvent.WARP_CANCELLED).playTo(getPlayer()); MMOCore.plugin.configManager.getSimpleMessage("warping-canceled").send(getPlayer()); giveStellium(waypoint.getStelliumCost(), PlayerResourceUpdateEvent.UpdateReason.SKILL_REGENERATION); cancel(); @@ -449,12 +450,12 @@ private final Map lastActivity = new HashMap<>(); if (t++ >= 100) { getPlayer().teleport(waypoint.getLocation()); getPlayer().addPotionEffect(new PotionEffect(PotionEffectType.BLINDNESS, 20, 1, false, false)); - MMOCore.plugin.soundManager.play(getPlayer(), SoundManager.SoundEvent.WARP_TELEPORT); + MMOCore.plugin.soundManager.getSound(SoundEvent.WARP_TELEPORT).playTo(getPlayer()); cancel(); return; } - MMOCore.plugin.soundManager.play(getPlayer(), SoundManager.SoundEvent.WARP_CHARGE, 1, (float) (t / Math.PI * .015 + .5)); + MMOCore.plugin.soundManager.getSound(SoundEvent.WARP_CHARGE).playTo(getPlayer(), 1, (float) (t / Math.PI * .015 + .5)); double r = Math.sin((double) t / 100 * Math.PI); for (double j = 0; j < Math.PI * 2; j += Math.PI / 4) getPlayer().getLocation().getWorld().spawnParticle(Particle.REDSTONE, @@ -532,7 +533,7 @@ private final Map lastActivity = new HashMap<>(); Bukkit.getPluginManager().callEvent(new PlayerLevelUpEvent(this, null, oldLevel, level)); if (isOnline()) { new ConfigMessage("level-up").addPlaceholders("level", "" + level).send(getPlayer()); - MMOCore.plugin.soundManager.play(getPlayer(), SoundManager.SoundEvent.LEVEL_UP); + MMOCore.plugin.soundManager.getSound(SoundEvent.LEVEL_UP).playTo(getPlayer()); new SmallParticleEffect(getPlayer(), Particle.SPELL_INSTANT); } getStats().updateStats(); @@ -675,6 +676,22 @@ private final Map lastActivity = new HashMap<>(); return skillCasting != null; } + public void setSkillCasting(SkillCastingHandler skillCasting) { + Validate.isTrue(!isCasting(), "Player already in casting mode"); + this.skillCasting = skillCasting; + } + + @NotNull + public SkillCastingHandler getSkillCasting() { + return Objects.requireNonNull(skillCasting, "Player not in casting mode"); + } + + public void leaveCastingMode() { + Validate.isTrue(isCasting(), "Player not in casting mode"); + skillCasting.close(); + this.skillCasting = null; + } + public void displayActionBar(String message) { if (!isOnline()) return; @@ -808,7 +825,7 @@ private final Map lastActivity = new HashMap<>(); * checks if they could potentially upgrade to one of these * * @return If the player can change its current class to - * a subclass + * a subclass */ public boolean canChooseSubclass() { for (Subclass subclass : getProfess().getSubclasses()) diff --git a/src/main/java/net/Indyuce/mmocore/api/player/social/Party.java b/src/main/java/net/Indyuce/mmocore/api/player/social/Party.java index c2cc27b1..9844d0cd 100644 --- a/src/main/java/net/Indyuce/mmocore/api/player/social/Party.java +++ b/src/main/java/net/Indyuce/mmocore/api/player/social/Party.java @@ -149,21 +149,18 @@ public class Party { new ArrayList<>(members).forEach(action); } - private static final String PARTY_BUFF_MODIFIER_KEY = "mmocoreParty"; - /** * Applies party stat bonuses to a specific player */ private void applyStatBonuses(PlayerData player) { - MMOCore.plugin.partyManager.getBonuses().forEach(stat -> player.getStats().getInstance(stat).addModifier(PARTY_BUFF_MODIFIER_KEY, - MMOCore.plugin.partyManager.getBonus(stat).multiply(members.size() - 1))); + MMOCore.plugin.partyManager.getBonuses().forEach(buff -> buff.multiply(members.size() - 1).register(player.getMMOPlayerData())); } /** * Clear party stat bonuses from a player */ private void clearStatBonuses(PlayerData player) { - MMOCore.plugin.partyManager.getBonuses().forEach(stat -> player.getStats().getInstance(stat).remove(PARTY_BUFF_MODIFIER_KEY)); + MMOCore.plugin.partyManager.getBonuses().forEach(buff -> buff.unregister(player.getMMOPlayerData())); } @Override diff --git a/src/main/java/net/Indyuce/mmocore/experience/PlayerProfessions.java b/src/main/java/net/Indyuce/mmocore/experience/PlayerProfessions.java index 8f72bf32..aeab17be 100644 --- a/src/main/java/net/Indyuce/mmocore/experience/PlayerProfessions.java +++ b/src/main/java/net/Indyuce/mmocore/experience/PlayerProfessions.java @@ -6,6 +6,7 @@ import com.google.gson.JsonObject; import io.lumine.mythic.lib.MythicLib; import net.Indyuce.mmocore.MMOCore; import net.Indyuce.mmocore.api.ConfigMessage; +import net.Indyuce.mmocore.api.SoundEvent; import net.Indyuce.mmocore.api.event.PlayerExperienceGainEvent; import net.Indyuce.mmocore.api.event.PlayerLevelUpEvent; import net.Indyuce.mmocore.api.player.PlayerData; @@ -195,7 +196,7 @@ public class PlayerProfessions { new SmallParticleEffect(playerData.getPlayer(), Particle.SPELL_INSTANT); new ConfigMessage("profession-level-up").addPlaceholders("level", "" + level, "profession", profession.getName()) .send(playerData.getPlayer()); - MMOCore.plugin.soundManager.play(playerData.getPlayer(), SoundManager.SoundEvent.LEVEL_UP); + MMOCore.plugin.soundManager.getSound(SoundEvent.LEVEL_UP).playTo(playerData.getPlayer()); playerData.getStats().updateStats(); // Apply profession experience table diff --git a/src/main/java/net/Indyuce/mmocore/experience/source/BrewPotionExperienceSource.java b/src/main/java/net/Indyuce/mmocore/experience/source/BrewPotionExperienceSource.java index 342faea4..2f804709 100644 --- a/src/main/java/net/Indyuce/mmocore/experience/source/BrewPotionExperienceSource.java +++ b/src/main/java/net/Indyuce/mmocore/experience/source/BrewPotionExperienceSource.java @@ -23,11 +23,14 @@ import java.util.List; import java.util.Optional; public class BrewPotionExperienceSource extends ExperienceSource { + private final double multiplier; private final List types = new ArrayList<>(); public BrewPotionExperienceSource(ExperienceDispenser dispenser, MMOLineConfig config) { super(dispenser); + multiplier = config.getDouble("multiplier", 1); + if (config.contains("effect")) for (String key : config.getString("effect").split(",")) types.add(PotionType.valueOf(key.toUpperCase().replace("-", "_"))); @@ -164,7 +167,7 @@ public class BrewPotionExperienceSource extends ExperienceSource { */ // exp += getTotal(mapEffectDurations()); - getDispenser().giveExperience(PlayerData.get(player), (int) exp, null); + getDispenser().giveExperience(PlayerData.get(player), (int) exp * multiplier, null); } } } diff --git a/src/main/java/net/Indyuce/mmocore/gui/AttributeView.java b/src/main/java/net/Indyuce/mmocore/gui/AttributeView.java index fb2348c3..49e74717 100644 --- a/src/main/java/net/Indyuce/mmocore/gui/AttributeView.java +++ b/src/main/java/net/Indyuce/mmocore/gui/AttributeView.java @@ -1,6 +1,7 @@ package net.Indyuce.mmocore.gui; import net.Indyuce.mmocore.MMOCore; +import net.Indyuce.mmocore.api.SoundEvent; import net.Indyuce.mmocore.api.event.PlayerAttributeUseEvent; import net.Indyuce.mmocore.api.player.PlayerData; import net.Indyuce.mmocore.api.player.attribute.PlayerAttribute; @@ -89,13 +90,13 @@ public class AttributeView extends EditableInventory { int spent = playerData.getAttributes().countSkillPoints(); if (spent < 1) { MMOCore.plugin.configManager.getSimpleMessage("no-attribute-points-spent").send(player); - MMOCore.plugin.soundManager.play(getPlayer(), SoundManager.SoundEvent.NOT_ENOUGH_POINTS); + MMOCore.plugin.soundManager.getSound(SoundEvent.NOT_ENOUGH_POINTS).playTo(getPlayer()); return; } if (playerData.getAttributeReallocationPoints() < 1) { MMOCore.plugin.configManager.getSimpleMessage("not-attribute-reallocation-point").send(player); - MMOCore.plugin.soundManager.play(getPlayer(), SoundManager.SoundEvent.NOT_ENOUGH_POINTS); + MMOCore.plugin.soundManager.getSound(SoundEvent.NOT_ENOUGH_POINTS).playTo(getPlayer()); return; } @@ -103,7 +104,7 @@ public class AttributeView extends EditableInventory { playerData.giveAttributePoints(spent); playerData.giveAttributeReallocationPoints(-1); MMOCore.plugin.configManager.getSimpleMessage("attribute-points-reallocated", "points", "" + playerData.getAttributePoints()).send(player); - MMOCore.plugin.soundManager.play(getPlayer(), SoundManager.SoundEvent.RESET_ATTRIBUTES); + MMOCore.plugin.soundManager.getSound(SoundEvent.RESET_ATTRIBUTES).playTo(getPlayer()); open(); } @@ -112,21 +113,21 @@ public class AttributeView extends EditableInventory { if (playerData.getAttributePoints() < 1) { MMOCore.plugin.configManager.getSimpleMessage("not-attribute-point").send(player); - MMOCore.plugin.soundManager.play(getPlayer(), SoundManager.SoundEvent.NOT_ENOUGH_POINTS); + MMOCore.plugin.soundManager.getSound(SoundEvent.NOT_ENOUGH_POINTS).playTo(getPlayer()); return; } AttributeInstance ins = playerData.getAttributes().getInstance(attribute); if (attribute.hasMax() && ins.getBase() >= attribute.getMax()) { MMOCore.plugin.configManager.getSimpleMessage("attribute-max-points-hit").send(player); - MMOCore.plugin.soundManager.play(getPlayer(), SoundManager.SoundEvent.NOT_ENOUGH_POINTS); + MMOCore.plugin.soundManager.getSound(SoundEvent.NOT_ENOUGH_POINTS).playTo(getPlayer()); return; } ins.addBase(1); playerData.giveAttributePoints(-1); MMOCore.plugin.configManager.getSimpleMessage("attribute-level-up", "attribute", attribute.getName(), "level", "" + ins.getBase()).send(player); - MMOCore.plugin.soundManager.play(getPlayer(), SoundManager.SoundEvent.LEVEL_ATTRIBUTE); + MMOCore.plugin.soundManager.getSound(SoundEvent.LEVEL_ATTRIBUTE).playTo(getPlayer()); PlayerAttributeUseEvent playerAttributeUseEvent = new PlayerAttributeUseEvent(playerData, attribute); Bukkit.getServer().getPluginManager().callEvent(playerAttributeUseEvent); diff --git a/src/main/java/net/Indyuce/mmocore/gui/ClassConfirmation.java b/src/main/java/net/Indyuce/mmocore/gui/ClassConfirmation.java index 5db7a70b..6e6c65ad 100644 --- a/src/main/java/net/Indyuce/mmocore/gui/ClassConfirmation.java +++ b/src/main/java/net/Indyuce/mmocore/gui/ClassConfirmation.java @@ -1,6 +1,7 @@ package net.Indyuce.mmocore.gui; import net.Indyuce.mmocore.MMOCore; +import net.Indyuce.mmocore.api.SoundEvent; import net.Indyuce.mmocore.api.event.PlayerChangeClassEvent; import net.Indyuce.mmocore.api.player.PlayerData; import net.Indyuce.mmocore.api.player.profess.PlayerClass; @@ -125,7 +126,7 @@ public class ClassConfirmation extends EditableInventory { (playerData.hasSavedClass(profess) ? playerData.getClassInfo(profess) : new SavedClassInformation(MMOCore.plugin.dataProvider.getDataManager().getDefaultData())).load(profess, playerData); MMOCore.plugin.configManager.getSimpleMessage("class-select", "class", profess.getName()).send(player); - MMOCore.plugin.soundManager.play(player, SoundManager.SoundEvent.SELECT_CLASS); + MMOCore.plugin.soundManager.getSound(SoundEvent.SELECT_CLASS).playTo(player); player.closeInventory(); } } diff --git a/src/main/java/net/Indyuce/mmocore/gui/ClassSelect.java b/src/main/java/net/Indyuce/mmocore/gui/ClassSelect.java index a8dedce5..dc62d6db 100644 --- a/src/main/java/net/Indyuce/mmocore/gui/ClassSelect.java +++ b/src/main/java/net/Indyuce/mmocore/gui/ClassSelect.java @@ -5,6 +5,7 @@ import io.lumine.mythic.lib.api.item.ItemTag; import io.lumine.mythic.lib.api.item.NBTItem; import net.Indyuce.mmocore.MMOCore; import net.Indyuce.mmocore.api.ConfigMessage; +import net.Indyuce.mmocore.api.SoundEvent; import net.Indyuce.mmocore.api.player.PlayerData; import net.Indyuce.mmocore.api.player.profess.ClassOption; import net.Indyuce.mmocore.api.player.profess.PlayerClass; @@ -109,14 +110,14 @@ public class ClassSelect extends EditableInventory { return; if (playerData.getClassPoints() < 1) { - MMOCore.plugin.soundManager.play(player, SoundManager.SoundEvent.CANT_SELECT_CLASS); + MMOCore.plugin.soundManager.getSound(SoundEvent.CANT_SELECT_CLASS).playTo(player); new ConfigMessage("cant-choose-new-class").send(player); return; } PlayerClass profess = MMOCore.plugin.classManager.get(tag); if (profess.equals(playerData.getProfess())) { - MMOCore.plugin.soundManager.play(player, SoundManager.SoundEvent.CANT_SELECT_CLASS); + MMOCore.plugin.soundManager.getSound(SoundEvent.CANT_SELECT_CLASS).playTo(player); MMOCore.plugin.configManager.getSimpleMessage("already-on-class", "class", profess.getName()).send(player); return; } diff --git a/src/main/java/net/Indyuce/mmocore/gui/QuestViewer.java b/src/main/java/net/Indyuce/mmocore/gui/QuestViewer.java index 02eff450..66e2193d 100644 --- a/src/main/java/net/Indyuce/mmocore/gui/QuestViewer.java +++ b/src/main/java/net/Indyuce/mmocore/gui/QuestViewer.java @@ -3,6 +3,7 @@ package net.Indyuce.mmocore.gui; import io.lumine.mythic.lib.api.item.ItemTag; import io.lumine.mythic.lib.api.item.NBTItem; import net.Indyuce.mmocore.MMOCore; +import net.Indyuce.mmocore.api.SoundEvent; import net.Indyuce.mmocore.experience.Profession; import net.Indyuce.mmocore.api.player.PlayerData; import net.Indyuce.mmocore.api.quest.Quest; @@ -235,7 +236,7 @@ public class QuestViewer extends EditableInventory { if (playerData.getQuestData().hasCurrent(quest)) { if (event.getAction() == InventoryAction.PICKUP_HALF) { playerData.getQuestData().start(null); - MMOCore.plugin.soundManager.play(player, SoundManager.SoundEvent.CANCEL_QUEST); + MMOCore.plugin.soundManager.getSound(SoundEvent.CANCEL_QUEST).playTo(player); MMOCore.plugin.configManager.getSimpleMessage("cancel-quest").send(player); open(); } @@ -293,7 +294,7 @@ public class QuestViewer extends EditableInventory { * eventually start a new quest. */ MMOCore.plugin.configManager.getSimpleMessage("start-quest", "quest", quest.getName()).send(player); - MMOCore.plugin.soundManager.play(player, SoundManager.SoundEvent.START_QUEST); + MMOCore.plugin.soundManager.getSound(SoundEvent.START_QUEST).playTo(player); playerData.getQuestData().start(quest); open(); } diff --git a/src/main/java/net/Indyuce/mmocore/gui/SubclassConfirmation.java b/src/main/java/net/Indyuce/mmocore/gui/SubclassConfirmation.java index d973a1d5..cd499e10 100644 --- a/src/main/java/net/Indyuce/mmocore/gui/SubclassConfirmation.java +++ b/src/main/java/net/Indyuce/mmocore/gui/SubclassConfirmation.java @@ -1,6 +1,7 @@ package net.Indyuce.mmocore.gui; import net.Indyuce.mmocore.MMOCore; +import net.Indyuce.mmocore.api.SoundEvent; import net.Indyuce.mmocore.api.event.PlayerChangeClassEvent; import net.Indyuce.mmocore.api.player.PlayerData; import net.Indyuce.mmocore.api.player.profess.PlayerClass; @@ -66,7 +67,7 @@ public class SubclassConfirmation extends EditableInventory { playerData.setClass(profess); MMOCore.plugin.configManager.getSimpleMessage("class-select", "class", profess.getName()).send(player); - MMOCore.plugin.soundManager.play(player, SoundManager.SoundEvent.SELECT_CLASS); + MMOCore.plugin.soundManager.getSound(SoundEvent.SELECT_CLASS).playTo(player); player.closeInventory(); } } diff --git a/src/main/java/net/Indyuce/mmocore/gui/SubclassSelect.java b/src/main/java/net/Indyuce/mmocore/gui/SubclassSelect.java index 57631bb8..d7080135 100644 --- a/src/main/java/net/Indyuce/mmocore/gui/SubclassSelect.java +++ b/src/main/java/net/Indyuce/mmocore/gui/SubclassSelect.java @@ -5,6 +5,7 @@ import io.lumine.mythic.lib.api.item.ItemTag; import io.lumine.mythic.lib.api.item.NBTItem; import net.Indyuce.mmocore.MMOCore; import net.Indyuce.mmocore.api.ConfigMessage; +import net.Indyuce.mmocore.api.SoundEvent; import net.Indyuce.mmocore.api.player.PlayerData; import net.Indyuce.mmocore.api.player.profess.PlayerClass; import net.Indyuce.mmocore.api.player.profess.Subclass; @@ -118,7 +119,7 @@ public class SubclassSelect extends EditableInventory { if (playerData.getClassPoints() < 1) { player.closeInventory(); - MMOCore.plugin.soundManager.play(getPlayer(), SoundManager.SoundEvent.CANT_SELECT_CLASS); + MMOCore.plugin.soundManager.getSound(SoundEvent.CANT_SELECT_CLASS).playTo(getPlayer()); new ConfigMessage("cant-choose-new-class").send(player); return; } diff --git a/src/main/java/net/Indyuce/mmocore/listener/WaypointsListener.java b/src/main/java/net/Indyuce/mmocore/listener/WaypointsListener.java index b4f21b78..b95d7c7c 100644 --- a/src/main/java/net/Indyuce/mmocore/listener/WaypointsListener.java +++ b/src/main/java/net/Indyuce/mmocore/listener/WaypointsListener.java @@ -1,5 +1,6 @@ package net.Indyuce.mmocore.listener; +import net.Indyuce.mmocore.api.SoundEvent; import net.Indyuce.mmocore.manager.SoundManager; import org.bukkit.Particle; import org.bukkit.entity.Player; @@ -29,7 +30,7 @@ public class WaypointsListener implements Listener { data.unlockWaypoint(waypoint); new SmallParticleEffect(player, Particle.SPELL_WITCH); MMOCore.plugin.configManager.getSimpleMessage("new-waypoint", "waypoint", waypoint.getName()).send(player); - MMOCore.plugin.soundManager.play(player, SoundManager.SoundEvent.WARP_UNLOCK); + MMOCore.plugin.soundManager.getSound(SoundEvent.WARP_UNLOCK).playTo(player); return; } diff --git a/src/main/java/net/Indyuce/mmocore/loot/chest/LootChest.java b/src/main/java/net/Indyuce/mmocore/loot/chest/LootChest.java index ea52030b..e20111db 100644 --- a/src/main/java/net/Indyuce/mmocore/loot/chest/LootChest.java +++ b/src/main/java/net/Indyuce/mmocore/loot/chest/LootChest.java @@ -1,6 +1,7 @@ package net.Indyuce.mmocore.loot.chest; import net.Indyuce.mmocore.MMOCore; +import net.Indyuce.mmocore.api.SoundEvent; import net.Indyuce.mmocore.manager.SoundManager; import org.bukkit.Location; import org.bukkit.Material; @@ -66,7 +67,7 @@ public class LootChest { // If a player is responsible of closing the chest, close it with sound if (player) { - MMOCore.plugin.soundManager.play(block.loc.getBlock(), SoundManager.SoundEvent.CLOSE_LOOT_CHEST); + MMOCore.plugin.soundManager.getSound(SoundEvent.CLOSE_LOOT_CHEST).playAt(block.loc); block.loc.getWorld().spawnParticle(Particle.CRIT, block.loc.clone().add(.5, .5, .5), 16, 0, 0, 0, .5); MMOCore.plugin.lootChests.unregister(this); } diff --git a/src/main/java/net/Indyuce/mmocore/manager/SoundManager.java b/src/main/java/net/Indyuce/mmocore/manager/SoundManager.java index 0f70949d..b64c47a5 100644 --- a/src/main/java/net/Indyuce/mmocore/manager/SoundManager.java +++ b/src/main/java/net/Indyuce/mmocore/manager/SoundManager.java @@ -1,72 +1,24 @@ package net.Indyuce.mmocore.manager; +import net.Indyuce.mmocore.api.SoundEvent; import net.Indyuce.mmocore.api.SoundObject; -import org.bukkit.Location; -import org.bukkit.block.Block; import org.bukkit.configuration.file.FileConfiguration; -import org.bukkit.entity.Player; +import org.jetbrains.annotations.NotNull; import java.util.HashMap; import java.util.Map; +import java.util.Objects; public class SoundManager { - private final Map sounds = new HashMap<>(); + private final Map sounds = new HashMap<>(); - public SoundManager(FileConfiguration config) { - for(SoundEvent sound : SoundEvent.values()) - sounds.put(sound, new SoundObject(config.getString(sound.name().replace("_", "-").toLowerCase()))); - } + public SoundManager(FileConfiguration config) { + for (SoundEvent sound : SoundEvent.values()) + sounds.put(sound, new SoundObject(config.getString(sound.name().replace("_", "-").toLowerCase()))); + } - - public void play(Block block, SoundEvent event) { - play(block, block.getLocation(), event); - } - - public void play(Block block, Location loc, SoundEvent event) { - SoundObject sound = sounds.get(event); - if(sound.hasSound()) - block.getWorld().playSound(loc, sound.getSound(), sound.getVolume(), sound.getPitch()); - else block.getWorld().playSound(loc, sound.getKey(), sound.getVolume(), sound.getPitch()); - } - - public void play(Player player, SoundEvent event) { - play(player, player.getLocation(), event); - } - - public void play(Player player, Location loc, SoundEvent event) { - SoundObject sound = sounds.get(event); - if(sound.hasSound()) - player.playSound(loc, sound.getSound(), sound.getVolume(), sound.getPitch()); - else player.playSound(loc, sound.getKey(), sound.getVolume(), sound.getPitch()); - } - - public void play(Player player, SoundEvent event, float volume, float pitch) { - play(player, player.getLocation(), event, volume, pitch); - } - - public void play(Player player, Location loc, SoundEvent event, float volume, float pitch) { - SoundObject sound = sounds.get(event); - if(sound.hasSound()) - player.playSound(loc, sound.getSound(), volume, pitch); - else player.playSound(loc, sound.getKey(), volume, pitch); - } - - public enum SoundEvent { - LEVEL_UP, - WARP_TELEPORT, - WARP_CANCELLED, - WARP_CHARGE, - WARP_UNLOCK, - HOTBAR_SWAP, - SPELL_CAST_BEGIN, - SPELL_CAST_END, - CANT_SELECT_CLASS, - SELECT_CLASS, - LEVEL_ATTRIBUTE, - RESET_ATTRIBUTES, - NOT_ENOUGH_POINTS, - CANCEL_QUEST, - START_QUEST, - CLOSE_LOOT_CHEST - } + @NotNull + public SoundObject getSound(SoundEvent event) { + return Objects.requireNonNull(sounds.get(event), "Could not find sound for " + event.name()); + } } diff --git a/src/main/java/net/Indyuce/mmocore/manager/data/PlayerDataManager.java b/src/main/java/net/Indyuce/mmocore/manager/data/PlayerDataManager.java index 780680b5..4c951239 100644 --- a/src/main/java/net/Indyuce/mmocore/manager/data/PlayerDataManager.java +++ b/src/main/java/net/Indyuce/mmocore/manager/data/PlayerDataManager.java @@ -32,9 +32,7 @@ public abstract class PlayerDataManager { * @return Player data, if it's loaded */ public PlayerData get(UUID uuid) { - if(isLoaded(uuid)) - return Objects.requireNonNull(data.get(uuid), "Player data is not loaded"); - else return setup(uuid); + return Objects.requireNonNull(data.get(uuid), "Player data is not loaded"); } /** diff --git a/src/main/java/net/Indyuce/mmocore/skill/cast/KeyCombo.java b/src/main/java/net/Indyuce/mmocore/skill/cast/KeyCombo.java new file mode 100644 index 00000000..874d8638 --- /dev/null +++ b/src/main/java/net/Indyuce/mmocore/skill/cast/KeyCombo.java @@ -0,0 +1,40 @@ +package net.Indyuce.mmocore.skill.cast; + +import java.util.ArrayList; +import java.util.List; +import java.util.Objects; + +/** + * There is one key combo per skill slot. This means + * that independently of both the player's class and + * the skill bounty to the n-th slot, the key combo to + * perform to cast the n-th skill is always the same + */ +public class KeyCombo { + private final List keys = new ArrayList<>(); + + public int countKeys() { + return keys.size(); + } + + public void registerKey(PlayerKey key) { + keys.add(key); + } + + public PlayerKey getAt(int index) { + return keys.get(index); + } + + @Override + public boolean equals(Object o) { + if (this == o) return true; + if (o == null || getClass() != o.getClass()) return false; + KeyCombo keyCombo = (KeyCombo) o; + return Objects.equals(keys, keyCombo.keys); + } + + @Override + public int hashCode() { + return keys.hashCode(); + } +} diff --git a/src/main/java/net/Indyuce/mmocore/skill/cast/PlayerKey.java b/src/main/java/net/Indyuce/mmocore/skill/cast/PlayerKey.java index c470b910..18e08d39 100644 --- a/src/main/java/net/Indyuce/mmocore/skill/cast/PlayerKey.java +++ b/src/main/java/net/Indyuce/mmocore/skill/cast/PlayerKey.java @@ -5,25 +5,40 @@ public enum PlayerKey { /** * When a player left clicks */ - LEFT_CLICK, + LEFT_CLICK(false), /** * When a player right clicks */ - RIGHT_CLICK, + RIGHT_CLICK(false), /** * When a player drops the item they are holding */ - DROP, + DROP(true), /** * When a player swaps their hand items */ - SWAP_HANDS, + SWAP_HANDS(true), /** * When a player sneaks (doesn't trigger when unsneaking) */ - CROUCH; + CROUCH(false); + + private final boolean cancellableEvent; + + private PlayerKey(boolean cancelableEvent) { + this.cancellableEvent = cancelableEvent; + } + + /** + * @return Whether or not the event causing the key press event + * should be cancelled when this key is actually being registered + * as a key combo action. + */ + public boolean shouldCancelEvent() { + return cancellableEvent; + } } diff --git a/src/main/java/net/Indyuce/mmocore/skill/cast/SkillCastingHandler.java b/src/main/java/net/Indyuce/mmocore/skill/cast/SkillCastingHandler.java new file mode 100644 index 00000000..bfdd854d --- /dev/null +++ b/src/main/java/net/Indyuce/mmocore/skill/cast/SkillCastingHandler.java @@ -0,0 +1,48 @@ +package net.Indyuce.mmocore.skill.cast; + +import net.Indyuce.mmocore.MMOCore; +import net.Indyuce.mmocore.api.player.PlayerData; +import org.apache.commons.lang.Validate; +import org.bukkit.Bukkit; +import org.bukkit.event.HandlerList; +import org.bukkit.event.Listener; +import org.bukkit.scheduler.BukkitRunnable; + +public abstract class SkillCastingHandler extends BukkitRunnable implements Listener { + private final PlayerData caster; + + private boolean open = true; + + public SkillCastingHandler(PlayerData caster, int runnablePeriod) { + this.caster = caster; + + runTaskTimer(MMOCore.plugin, 0, runnablePeriod); + Bukkit.getPluginManager().registerEvents(this, MMOCore.plugin); + } + + public PlayerData getCaster() { + return caster; + } + + public void close() { + Validate.isTrue(open, "Skill casting already ended"); + + open = false; + + // Unregister listeners + HandlerList.unregisterAll(this); + + // Cancel runnable + cancel(); + } + + @Override + public void run() { + if (!caster.isOnline() || caster.getPlayer().isDead()) + caster.leaveCastingMode(); + else + onTick(); + } + + public abstract void onTick(); +} diff --git a/src/main/java/net/Indyuce/mmocore/skill/cast/CastingMethod.java b/src/main/java/net/Indyuce/mmocore/skill/cast/SkillCastingMode.java similarity index 57% rename from src/main/java/net/Indyuce/mmocore/skill/cast/CastingMethod.java rename to src/main/java/net/Indyuce/mmocore/skill/cast/SkillCastingMode.java index a43f15d3..88a0cc39 100644 --- a/src/main/java/net/Indyuce/mmocore/skill/cast/CastingMethod.java +++ b/src/main/java/net/Indyuce/mmocore/skill/cast/SkillCastingMode.java @@ -1,12 +1,14 @@ package net.Indyuce.mmocore.skill.cast; +import net.Indyuce.mmocore.skill.cast.listener.KeyCombos; import net.Indyuce.mmocore.skill.cast.listener.SkillBar; +import net.Indyuce.mmocore.skill.cast.listener.SkillScroller; import org.bukkit.configuration.ConfigurationSection; import org.bukkit.event.Listener; import java.util.function.Function; -public enum CastingMethod { +public enum SkillCastingMode { /** * The first ever casting method to be implemented in MMOCore. @@ -14,14 +16,19 @@ public enum CastingMethod { * When pressing a key, the list of bound skills display on the * action bar */ - SKILL_BAR(config-> new SkillBar(config)), - - SKILL_SCROLL; + SKILL_BAR(config -> new SkillBar(config)), /** - * Initialize your skill combo by pressing some key + * TODO */ - KEY_COMBOS(), + SKILL_SCROLL(config -> new SkillScroller(config)), + + /** + * Initialize your skill combo by pressing some key. + *

+ * Then press a certain amount of keys to + */ + KEY_COMBOS(config -> new KeyCombos(config)), /** * Not implemented yet. @@ -30,7 +37,7 @@ public enum CastingMethod { * a book with all the skills displayed into it and click * some clickable text to cast the skill. */ - SPELL_BOOK(), + // SPELL_BOOK(null), /** * Not implemented yet. @@ -38,11 +45,17 @@ public enum CastingMethod { * Much like the spell book but using a custom GUI instead * of a spell book to display the available skills. */ - SPELL_GUI(); + // SPELL_GUI(null), + + ; private final Function listenerLoader; - CastingMethod(Function listenerLoader) { + SkillCastingMode(Function listenerLoader) { this.listenerLoader = listenerLoader; } + + public Listener loadFromConfig(ConfigurationSection config) { + return listenerLoader.apply(config); + } } diff --git a/src/main/java/net/Indyuce/mmocore/skill/cast/listener/KeyCombos.java b/src/main/java/net/Indyuce/mmocore/skill/cast/listener/KeyCombos.java new file mode 100644 index 00000000..d163d5ff --- /dev/null +++ b/src/main/java/net/Indyuce/mmocore/skill/cast/listener/KeyCombos.java @@ -0,0 +1,193 @@ +package net.Indyuce.mmocore.skill.cast.listener; + +import io.lumine.mythic.lib.MythicLib; +import io.lumine.mythic.lib.UtilityMethods; +import io.lumine.mythic.lib.api.player.EquipmentSlot; +import io.lumine.mythic.lib.player.PlayerMetadata; +import io.lumine.mythic.lib.skill.trigger.TriggerMetadata; +import net.Indyuce.mmocore.MMOCore; +import net.Indyuce.mmocore.api.SoundObject; +import net.Indyuce.mmocore.api.event.PlayerKeyPressEvent; +import net.Indyuce.mmocore.api.player.PlayerData; +import net.Indyuce.mmocore.skill.cast.KeyCombo; +import net.Indyuce.mmocore.skill.cast.PlayerKey; +import net.Indyuce.mmocore.skill.cast.SkillCastingHandler; +import org.apache.commons.lang.Validate; +import org.bukkit.configuration.ConfigurationSection; +import org.bukkit.entity.Player; +import org.bukkit.event.EventHandler; +import org.bukkit.event.Listener; + +import javax.annotation.Nullable; +import java.util.HashMap; +import java.util.Map; +import java.util.Objects; +import java.util.logging.Level; + +public class KeyCombos implements Listener { + + /** + * Using instances of KeyCombo as keys work because + * {@link KeyCombo} has a working implementation for the + * hash code method + */ + private final Map combos = new HashMap<>(); + + /** + * Key players need to press to start a combo + */ + private final PlayerKey initializerKey; + private final int longestCombo; + + /** + * Handles the display of the action bar when casting a skill. + * Set to null if disabled + */ + @Nullable + private final ActionBarOptions actionBarOptions; + + @Nullable + private final SoundObject beginComboSound, comboClickSound, failComboSound; + + /** + * Essentially the inverse of the {@link #combos} map. This maps + * the skill slot to the corresponding key combo. There's no problem + * because the maps are 100% bijective + */ + private static final Map PUBLIC_COMBOS = new HashMap<>(); + + public KeyCombos(ConfigurationSection config) { + + int longestCombo = 0; + + // Load different combos + for (String key : config.getConfigurationSection("combos").getKeys(false)) + try { + int spellSlot = Integer.valueOf(key); + Validate.isTrue(spellSlot >= 0, "Spell slot must be at least 0"); + Validate.isTrue(!combos.values().contains(spellSlot), "There is already a key combo with the same skill slot"); + KeyCombo combo = new KeyCombo(); + for (String str : config.getStringList("combos." + key)) + combo.registerKey(PlayerKey.valueOf(UtilityMethods.enumName(str))); + + combos.put(combo, spellSlot); + longestCombo = Math.max(longestCombo, combo.countKeys()); + + PUBLIC_COMBOS.put(spellSlot, combo); + } catch (RuntimeException exception) { + MMOCore.plugin.getLogger().log(Level.WARNING, "Could not load key combo '" + key + "': " + exception.getMessage()); + } + + this.longestCombo = longestCombo; + + // Load player key names + actionBarOptions = config.contains("action-bar") ? new ActionBarOptions(config.getConfigurationSection("action-bar")) : null; + + // Load sounds + beginComboSound = config.contains("sound.begin-combo") ? new SoundObject(config.getConfigurationSection("sound.begin-combo")) : null; + comboClickSound = config.contains("sound.combo-key") ? new SoundObject(config.getConfigurationSection("sound.combo-key")) : null; + failComboSound = config.contains("sound.fail-combo") ? new SoundObject(config.getConfigurationSection("sound.fail-combo")) : null; + + // Find initializer key + initializerKey = PlayerKey.valueOf(UtilityMethods.enumName(Objects.requireNonNull(config.getString("initializer-key"), "Could not find initializer key"))); + } + + @EventHandler + public void whenPressingKey(PlayerKeyPressEvent event) { + PlayerData playerData = event.getData(); + Player player = playerData.getPlayer(); + + if (!event.getData().isCasting()) { + if (event.getPressed() == initializerKey) { + + // Cancel event if necessary + if (event.getPressed().shouldCancelEvent()) + event.setCancelled(true); + + // Start combo + playerData.setSkillCasting(new CustomSkillCastingHandler(playerData)); + if (beginComboSound != null) + beginComboSound.playTo(player); + } + return; + } + + // Adding pressed key + CustomSkillCastingHandler casting = (CustomSkillCastingHandler) playerData.getSkillCasting(); + casting.current.registerKey(event.getPressed()); + casting.onTick(); + if (comboClickSound != null) + comboClickSound.playTo(player); + + // Cancel event if necessary + if (event.getPressed().shouldCancelEvent()) + event.setCancelled(true); + + // Hash current combo and check + if (combos.containsKey(casting.current)) { + int spellSlot = combos.get(casting.current) - 1; + playerData.leaveCastingMode(); + + // Cast spell + if (playerData.hasSkillBound(spellSlot)) { + PlayerMetadata caster = playerData.getMMOPlayerData().getStatMap().cache(EquipmentSlot.MAIN_HAND); + playerData.getBoundSkill(spellSlot).toCastable(playerData).cast(new TriggerMetadata(caster, null, null)); + } + return; + } + + // Check if current combo is too large + if (casting.current.countKeys() >= longestCombo) { + playerData.leaveCastingMode(); + if (failComboSound != null) + failComboSound.playTo(player); + } + } + + private class CustomSkillCastingHandler extends SkillCastingHandler { + private final KeyCombo current = new KeyCombo(); + + CustomSkillCastingHandler(PlayerData caster) { + super(caster, 10); + } + + @Override + public void onTick() { + if (actionBarOptions != null) + getCaster().displayActionBar(actionBarOptions.format(current)); + } + } + + private class ActionBarOptions { + private final String separator, noKey; + + /** + * Saves the names for all the players keys. Used when displaying + * the current player's key combo on the action bar + */ + private final Map keyNames = new HashMap<>(); + + ActionBarOptions(ConfigurationSection config) { + this.separator = Objects.requireNonNull(config.getString("separator"), "Could not find action bar option 'separator'"); + this.noKey = Objects.requireNonNull(config.getString("no-key"), "Could not find action bar option 'no-key'"); + + for (PlayerKey key : PlayerKey.values()) + keyNames.put(key, Objects.requireNonNull(config.getString("key-name." + key.name()), "Could not find translation for key " + key.name())); + } + + public String format(KeyCombo currentCombo) { + + // Join all keys with separator + String builder = currentCombo.countKeys() == 0 ? noKey : keyNames.get(currentCombo.getAt(0)); + int j = 1; + for (; j < currentCombo.countKeys(); j++) + builder += separator + keyNames.get(currentCombo.getAt(j)); + + // All remaining + for (; j < longestCombo; j++) + builder += separator + noKey; + + return MythicLib.plugin.parseColors(builder); + } + } +} diff --git a/src/main/java/net/Indyuce/mmocore/skill/cast/listener/SkillBar.java b/src/main/java/net/Indyuce/mmocore/skill/cast/listener/SkillBar.java index 8190076e..643db2e7 100644 --- a/src/main/java/net/Indyuce/mmocore/skill/cast/listener/SkillBar.java +++ b/src/main/java/net/Indyuce/mmocore/skill/cast/listener/SkillBar.java @@ -4,24 +4,20 @@ import io.lumine.mythic.lib.api.player.EquipmentSlot; import io.lumine.mythic.lib.player.PlayerMetadata; import io.lumine.mythic.lib.skill.trigger.TriggerMetadata; import net.Indyuce.mmocore.MMOCore; +import net.Indyuce.mmocore.api.SoundEvent; import net.Indyuce.mmocore.api.event.PlayerKeyPressEvent; import net.Indyuce.mmocore.api.player.PlayerData; -import net.Indyuce.mmocore.listener.event.PlayerPressKeyListener; import net.Indyuce.mmocore.manager.ConfigManager; -import net.Indyuce.mmocore.manager.SoundManager; import net.Indyuce.mmocore.skill.ClassSkill; import net.Indyuce.mmocore.skill.cast.PlayerKey; -import org.bukkit.Bukkit; +import net.Indyuce.mmocore.skill.cast.SkillCastingHandler; import org.bukkit.GameMode; import org.bukkit.configuration.ConfigurationSection; import org.bukkit.entity.Player; import org.bukkit.event.EventHandler; -import org.bukkit.event.HandlerList; import org.bukkit.event.Listener; import org.bukkit.event.player.PlayerItemHeldEvent; import org.bukkit.event.player.PlayerSwapHandItemsEvent; -import org.bukkit.inventory.ItemStack; -import org.bukkit.scheduler.BukkitRunnable; import java.util.Objects; @@ -38,7 +34,8 @@ public class SkillBar implements Listener { return; // Always cancel event - event.setCancelled(true); + if (event.getPressed().shouldCancelEvent()) + event.setCancelled(true); // Enter spell casting Player player = event.getData().getPlayer(); @@ -46,15 +43,13 @@ public class SkillBar implements Listener { if (player.getGameMode() != GameMode.SPECTATOR && (MMOCore.plugin.configManager.canCreativeCast || player.getGameMode() != GameMode.CREATIVE) && !playerData.isCasting() - && playerData.getBoundSkills().size() > 0) { - playerData.skillCasting = new SkillCasting(playerData); - MMOCore.plugin.soundManager.play(player, SoundManager.SoundEvent.SPELL_CAST_BEGIN); + && !playerData.getBoundSkills().isEmpty()) { + playerData.setSkillCasting(new CustomSkillCastingHandler(playerData)); + MMOCore.plugin.soundManager.getSound(SoundEvent.SPELL_CAST_BEGIN).playTo(player); } } - public class SkillCasting extends BukkitRunnable implements Listener { - private final PlayerData playerData; - + private class CustomSkillCastingHandler extends SkillCastingHandler { private final String ready = MMOCore.plugin.configManager.getSimpleMessage("casting.action-bar.ready").message(); private final String onCooldown = MMOCore.plugin.configManager.getSimpleMessage("casting.action-bar.on-cooldown").message(); private final String noMana = MMOCore.plugin.configManager.getSimpleMessage("casting.action-bar.no-mana").message(); @@ -62,18 +57,15 @@ public class SkillBar implements Listener { private int j; - public SkillCasting(PlayerData playerData) { - this.playerData = playerData; - - Bukkit.getPluginManager().registerEvents(this, MMOCore.plugin); - runTaskTimer(MMOCore.plugin, 0, 1); + CustomSkillCastingHandler(PlayerData playerData) { + super(playerData, 1); } @EventHandler() public void onSkillCast(PlayerItemHeldEvent event) { Player player = event.getPlayer(); - if (!playerData.isOnline()) return; - if (!event.getPlayer().equals(playerData.getPlayer())) + if (!getCaster().isOnline()) return; + if (!event.getPlayer().equals(getCaster().getPlayer())) return; /* @@ -92,9 +84,9 @@ public class SkillBar implements Listener { * cancelling the first one, the player held item slot must go back * to the previous one. */ - if (slot >= 0 && playerData.hasSkillBound(slot)) { - PlayerMetadata caster = playerData.getMMOPlayerData().getStatMap().cache(EquipmentSlot.MAIN_HAND); - playerData.getBoundSkill(slot).toCastable(playerData).cast(new TriggerMetadata(caster, null, null)); + if (slot >= 0 && getCaster().hasSkillBound(slot)) { + PlayerMetadata caster = getCaster().getMMOPlayerData().getStatMap().cache(EquipmentSlot.MAIN_HAND); + getCaster().getBoundSkill(slot).toCastable(getCaster()).cast(new TriggerMetadata(caster, null, null)); } } @@ -104,20 +96,14 @@ public class SkillBar implements Listener { ConfigManager.SwapAction action = player.isSneaking() ? MMOCore.plugin.configManager.sneakingSwapAction : MMOCore.plugin.configManager.normalSwapAction; - if (action != ConfigManager.SwapAction.SPELL_CAST || !playerData.isOnline()) return; - if (event.getPlayer().equals(playerData.getPlayer())) { - MMOCore.plugin.soundManager.play(player, SoundManager.SoundEvent.SPELL_CAST_END); - MMOCore.plugin.configManager.getSimpleMessage("casting.no-longer").send(playerData.getPlayer()); - close(); + if (action != ConfigManager.SwapAction.SPELL_CAST || !getCaster().isOnline()) return; + if (event.getPlayer().equals(getCaster().getPlayer())) { + MMOCore.plugin.soundManager.getSound(SoundEvent.SPELL_CAST_END).playTo(player); + MMOCore.plugin.configManager.getSimpleMessage("casting.no-longer").send(getCaster().getPlayer()); + PlayerData.get(player).leaveCastingMode(); } } - private void close() { - playerData.skillCasting = null; - HandlerList.unregisterAll(this); - cancel(); - } - private String getFormat(PlayerData data) { StringBuilder str = new StringBuilder(); if (!data.isOnline()) return str.toString(); @@ -146,19 +132,14 @@ public class SkillBar implements Listener { } @Override - public void run() { - if (!playerData.isOnline() || playerData.getPlayer().isDead()) { - close(); - return; - } - + public void onTick() { if (j % 20 == 0) - playerData.displayActionBar(getFormat(playerData)); + getCaster().displayActionBar(getFormat(getCaster())); for (int k = 0; k < 2; k++) { double a = (double) j++ / 5; - playerData.getProfess().getCastParticle() - .display(playerData.getPlayer().getLocation().add(Math.cos(a), 1 + Math.sin(a / 3) / 1.3, Math.sin(a))); + getCaster().getProfess().getCastParticle() + .display(getCaster().getPlayer().getLocation().add(Math.cos(a), 1 + Math.sin(a / 3) / 1.3, Math.sin(a))); } } } diff --git a/src/main/java/net/Indyuce/mmocore/skill/cast/listener/SkillScroller.java b/src/main/java/net/Indyuce/mmocore/skill/cast/listener/SkillScroller.java new file mode 100644 index 00000000..f9fdd078 --- /dev/null +++ b/src/main/java/net/Indyuce/mmocore/skill/cast/listener/SkillScroller.java @@ -0,0 +1,135 @@ +package net.Indyuce.mmocore.skill.cast.listener; + +import io.lumine.mythic.lib.UtilityMethods; +import io.lumine.mythic.lib.api.player.EquipmentSlot; +import io.lumine.mythic.lib.player.PlayerMetadata; +import io.lumine.mythic.lib.skill.trigger.TriggerMetadata; +import net.Indyuce.mmocore.api.SoundObject; +import net.Indyuce.mmocore.api.event.PlayerKeyPressEvent; +import net.Indyuce.mmocore.api.player.PlayerData; +import net.Indyuce.mmocore.skill.cast.PlayerKey; +import net.Indyuce.mmocore.skill.cast.SkillCastingHandler; +import org.bukkit.configuration.ConfigurationSection; +import org.bukkit.entity.Player; +import org.bukkit.event.EventHandler; +import org.bukkit.event.Listener; +import org.bukkit.event.player.PlayerItemHeldEvent; + +import javax.annotation.Nullable; +import java.util.Objects; + +public class SkillScroller implements Listener { + + /** + * Key players need to press to start casting + */ + private final PlayerKey enterKey, castKey; + + @Nullable + private final SoundObject enterSound, changeSound, leaveSound; + + public SkillScroller(ConfigurationSection config) { + + // Load sounds + enterSound = config.contains("sound.enter") ? new SoundObject(config.getConfigurationSection("sound.enter")) : null; + changeSound = config.contains("sound.change") ? new SoundObject(config.getConfigurationSection("sound.change")) : null; + leaveSound = config.contains("sound.leave") ? new SoundObject(config.getConfigurationSection("sound.leave")) : null; + + // Find keybinds + enterKey = PlayerKey.valueOf(UtilityMethods.enumName(Objects.requireNonNull(config.getString("enter-key"), "Could not find enter key"))); + castKey = PlayerKey.valueOf(UtilityMethods.enumName(Objects.requireNonNull(config.getString("cast-key"), "Could not find cast key"))); + } + + @EventHandler + public void whenPressingKey(PlayerKeyPressEvent event) { + PlayerData playerData = event.getData(); + Player player = playerData.getPlayer(); + + if (event.getPressed() == enterKey) { + + // Leave casting mode + if (playerData.isCasting()) { + + // Cancel event if necessary + if (event.getPressed().shouldCancelEvent()) + event.setCancelled(true); + + playerData.leaveCastingMode(); + if (leaveSound != null) + leaveSound.playTo(player); + return; + } + + // Check if there are skills bound + if (playerData.getBoundSkills().isEmpty()) + return; + + // Cancel event if necessary + if (event.getPressed().shouldCancelEvent()) + event.setCancelled(true); + + // Enter casting mode + playerData.setSkillCasting(new CustomSkillCastingHandler(playerData)); + if (enterSound != null) + enterSound.playTo(player); + } + + if (event.getPressed() == castKey && playerData.isCasting()) { + + // Cancel event if necessary + if (event.getPressed().shouldCancelEvent()) + event.setCancelled(true); + + CustomSkillCastingHandler casting = (CustomSkillCastingHandler) playerData.getSkillCasting(); + PlayerMetadata caster = playerData.getMMOPlayerData().getStatMap().cache(EquipmentSlot.MAIN_HAND); + playerData.getBoundSkill(casting.index).toCastable(playerData).cast(new TriggerMetadata(caster, null, null)); + } + } + + @EventHandler + public void onScroll(PlayerItemHeldEvent event) { + PlayerData playerData = PlayerData.get(event.getPlayer()); + if (!playerData.isCasting()) + return; + + if (playerData.getBoundSkills().isEmpty()) { + playerData.leaveCastingMode(); + return; + } + + event.setCancelled(true); + + int previous = event.getPreviousSlot(), current = event.getNewSlot(); + int dist1 = 9 + current - previous, dist2 = current - previous, dist3 = current - previous - 9; + int change = Math.abs(dist1) < Math.abs(dist2) ? (Math.abs(dist1) < Math.abs(dist3) ? dist1 : dist3) : (Math.abs(dist3) < Math.abs(dist2) ? dist3 : dist2); + + // Scroll trough items + CustomSkillCastingHandler casting = (CustomSkillCastingHandler) playerData.getSkillCasting(); + casting.index = mod(casting.index + change, playerData.getBoundSkills().size()); + casting.onTick(); + } + + private int mod(int x, int n) { + + while (x < 0) + x += n; + + while (x >= n) + x -= n; + + return x; + } + + private class CustomSkillCastingHandler extends SkillCastingHandler { + private int index = 0; + + CustomSkillCastingHandler(PlayerData caster) { + super(caster, 10); + } + + @Override + public void onTick() { + getCaster().displayActionBar("CLICK: " + getCaster().getBoundSkill(index).getSkill().getName()); + } + } +} diff --git a/src/main/resources/config.yml b/src/main/resources/config.yml index 2198b14f..a3306b47 100644 --- a/src/main/resources/config.yml +++ b/src/main/resources/config.yml @@ -62,6 +62,47 @@ protect-custom-mine: false # provide the player with experience points or not should-cobblestone-generators-give-exp: false +# Edit how to cast skills here. This part of the config is +# pretty tricky so it's best to read the wiki page before editing anything +skill-casting: + mode: KEY_COMBOS + initializer-key: SWAP_HANDS + sound: + begin-combo: + sound: BLOCK_END_PORTAL_FRAME_FILL + volume: 1 + pitch: 2 + combo-key: + sound: BLOCK_LEVER_CLICK + volume: 1 + pitch: 2 + fail-combo: + sound: BLOCK_FIRE_EXTINGUISH + volume: 1 + pitch: 2 + action-bar: + separator: ' - ' + no-key: '***' + key-name: + LEFT_CLICK: 'LEFT' + RIGHT_CLICK: 'RGHT' + DROP: 'DROP' + SWAP_HANDS: 'SWAP' + CROUCH: 'SHFT' + combos: + '1': + - LEFT_CLICK + - LEFT_CLICK + '2': + - LEFT_CLICK + - RIGHT_CLICK + '3': + - RIGHT_CLICK + - LEFT_CLICK + '4': + - RIGHT_CLICK + - RIGHT_CLICK + loot-chests: # Time in seconds it takes for a loot chest to @@ -140,17 +181,19 @@ death-exp-loss: # Percentage of current EXP you lose when dying. percent: 30 -# Modify the keybinds for the 'swap hand items' key (F by default) -# -# Available actions: -# - spell_cast (enters the default spell casting mode) -# - vanilla (swaps hand items) -# - hotbar_swap (swap the player's horbat with the 9 lowest inventory slots) -# -# If the action is invalid, it will use 'vanilla' by default -swap-keybind: - normal: spell_cast - sneaking: hotbar_swap +# Fun extra RPG feature that switches the player's hotbar with +# the 9 lower row items of his inventory. This allows the player +# to have two different item sets or quickly have access to pots +# TODO +hotbar-swapping: + enabled: true + + # Keybind which triggers + # Available keybinds + keybind: SWAP_HANDS + + # If the player has to sneak to swap hotbars + crouching: true # Set this to true to allow players # in creative mode to enter casting mode