Fixed permanent skills not triggering when locked/unlocked

This commit is contained in:
Jules 2024-05-09 18:01:36 -07:00
parent 952b543b45
commit 3df5676304
13 changed files with 128 additions and 77 deletions

View File

@ -207,8 +207,6 @@ public class MMOCore extends MMOPlugin {
if (Bukkit.getPluginManager().getPlugin("MMOMana") != null) { 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 + "MMOCore is not meant to be used with MMOItems ManaAndStamina");
getLogger().log(Level.SEVERE, ChatColor.DARK_RED + "Please read the installation guide!"); 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; return;
} }

View File

@ -393,11 +393,11 @@ public class PlayerData extends SynchronizedDataHolder implements OfflinePlayerD
*/ */
public boolean unlock(Unlockable unlockable) { public boolean unlock(Unlockable unlockable) {
Validate.isTrue(!unlockable.isUnlockedByDefault(), "Cannot unlock an item unlocked by default"); Validate.isTrue(!unlockable.isUnlockedByDefault(), "Cannot unlock an item unlocked by default");
unlockable.whenUnlocked(this);
final boolean wasLocked = unlockedItems.add(unlockable.getUnlockNamespacedKey()); final boolean wasLocked = unlockedItems.add(unlockable.getUnlockNamespacedKey());
// Call the event synchronously if (wasLocked) {
if (wasLocked) unlockable.whenUnlocked(this);
Bukkit.getScheduler().runTask(MythicLib.plugin, () -> Bukkit.getPluginManager().callEvent(new ItemUnlockedEvent(this, unlockable.getUnlockNamespacedKey()))); Bukkit.getScheduler().runTask(MythicLib.plugin, () -> Bukkit.getPluginManager().callEvent(new ItemUnlockedEvent(this, unlockable.getUnlockNamespacedKey())));
}
return wasLocked; return wasLocked;
} }
@ -409,11 +409,11 @@ public class PlayerData extends SynchronizedDataHolder implements OfflinePlayerD
*/ */
public boolean lock(Unlockable unlockable) { public boolean lock(Unlockable unlockable) {
Validate.isTrue(!unlockable.isUnlockedByDefault(), "Cannot lock an item unlocked by default"); Validate.isTrue(!unlockable.isUnlockedByDefault(), "Cannot lock an item unlocked by default");
unlockable.whenLocked(this);
boolean wasUnlocked = unlockedItems.remove(unlockable.getUnlockNamespacedKey()); boolean wasUnlocked = unlockedItems.remove(unlockable.getUnlockNamespacedKey());
if (wasUnlocked) if (wasUnlocked) {
//Calls the event synchronously unlockable.whenLocked(this);
Bukkit.getScheduler().runTask(MythicLib.plugin, () -> Bukkit.getPluginManager().callEvent(new ItemLockedEvent(this, unlockable.getUnlockNamespacedKey()))); Bukkit.getScheduler().runTask(MythicLib.plugin, () -> Bukkit.getPluginManager().callEvent(new ItemLockedEvent(this, unlockable.getUnlockNamespacedKey())));
}
return wasUnlocked; return wasUnlocked;
} }
@ -1224,19 +1224,37 @@ public class PlayerData extends SynchronizedDataHolder implements OfflinePlayerD
Validate.notNull(skill, "Skill cannot be null"); Validate.notNull(skill, "Skill cannot be null");
if (slot <= 0) return; if (slot <= 0) return;
// Friendly error in case server owner makes a skill permanent while players have already bound it
if (skill.isPermanent()) {
MMOCore.plugin.getLogger().log(Level.WARNING, "Attempted to bind permanent skill " + skill.getSkill().getName() + " to player " + getUniqueId());
return;
}
// Unbinds the previous skill (important for passive skills) // Unbinds the previous skill (important for passive skills)
unbindSkill(slot); unbindSkill(slot);
final SkillSlot skillSlot = getProfess().getSkillSlot(slot); final SkillSlot skillSlot = getProfess().getSkillSlot(slot);
boundSkills.put(slot, new BoundSkillInfo(skillSlot, skill, this)); boundSkills.put(slot, new BoundSkillInfo(skillSlot, skill, this));
} }
public void unbindSkill(int slot) { @Nullable
public BoundSkillInfo unbindSkill(int slot) {
final @Nullable BoundSkillInfo boundSkillInfo = boundSkills.remove(slot); final @Nullable BoundSkillInfo boundSkillInfo = boundSkills.remove(slot);
if (boundSkillInfo != null) boundSkillInfo.close(); if (boundSkillInfo != null) boundSkillInfo.close();
return boundSkillInfo;
} }
public List<ClassSkill> getBoundSkills() { @NotNull
return boundSkills.values().stream().map(BoundSkillInfo::getClassSkill).collect(Collectors.toList()); public Map<Integer, BoundSkillInfo> getBoundSkills() {
return boundSkills;
}
/**
* @return If the player has at least one active skill bound
*/
public boolean hasActiveSkillBound() {
for (BoundSkillInfo bound : boundSkills.values())
if (!bound.isPassive()) return true;
return false;
} }
@NotNull @NotNull

View File

@ -103,10 +103,9 @@ public class PlayerStats {
// Updates the player's unbindable CLASS passive skills // Updates the player's unbindable CLASS passive skills
final PassiveSkillMap skillMap = data.getMMOPlayerData().getPassiveSkillMap(); final PassiveSkillMap skillMap = data.getMMOPlayerData().getPassiveSkillMap();
skillMap.removeModifiers("MMOCorePassiveSkillNotBound"); skillMap.removeModifiers("MMOCorePermanentSkill");
for (ClassSkill skill : data.getProfess().getSkills()) for (ClassSkill skill : data.getProfess().getSkills())
if (!skill.needsBound() if (skill.isPermanent()
&& skill.getSkill().getTrigger().isPassive()
&& skill.getSkill().getTrigger() != TriggerType.LOGIN && skill.getSkill().getTrigger() != TriggerType.LOGIN
&& data.hasUnlocked(skill) && data.hasUnlocked(skill)
&& data.hasUnlockedLevel(skill)) && data.hasUnlockedLevel(skill))

View File

@ -23,14 +23,12 @@ public class UnlockSkillTrigger extends Trigger implements Removable {
@Override @Override
public void apply(PlayerData playerData) { public void apply(PlayerData playerData) {
final @Nullable ClassSkill found = playerData.getProfess().getSkill(skill); final @Nullable ClassSkill found = playerData.getProfess().getSkill(skill);
if (found != null) if (found != null) playerData.unlock(found);
playerData.unlock(found);
} }
@Override @Override
public void remove(PlayerData playerData) { public void remove(PlayerData playerData) {
final @Nullable ClassSkill found = playerData.getProfess().getSkill(skill); final @Nullable ClassSkill found = playerData.getProfess().getSkill(skill);
if (found != null) if (found != null) playerData.lock(found);
playerData.lock(found);
} }
} }

View File

@ -13,26 +13,23 @@ public class UnlockSlotTrigger extends Trigger implements Removable {
public UnlockSlotTrigger(MMOLineConfig config) { public UnlockSlotTrigger(MMOLineConfig config) {
super(config); super(config);
config.validateKeys("slot"); config.validateKeys("slot");
try { try {
slot = Integer.parseInt(config.getString("slot")); slot = Integer.parseInt(config.getString("slot"));
}catch(NumberFormatException e){ } catch (NumberFormatException exception) {
throw new IllegalArgumentException("The slot should be a number"); throw new IllegalArgumentException("Slot should be a number");
} }
Validate.isTrue(slot > 0, "Slot number must be positive"); Validate.isTrue(slot > 0, "Slot number must be positive");
} }
@Override @Override
public void apply(PlayerData player) { public void apply(PlayerData player) {
final SkillSlot skillSlot = player.getProfess().getSkillSlot(slot); player.unlock(player.getProfess().getSkillSlot(slot));
if (!player.hasUnlocked(skillSlot))
player.unlock(skillSlot);
} }
@Override @Override
public void remove(PlayerData player) { public void remove(PlayerData player) {
final SkillSlot skillSlot = player.getProfess().getSkillSlot(slot); player.lock(player.getProfess().getSkillSlot(slot));
if (player.hasUnlocked(skillSlot))
player.lock(skillSlot);
} }
} }

View File

@ -77,7 +77,7 @@ public class SkillCommandTreeNode extends CommandTreeNode {
} }
int value = change.apply(playerData.getSkillLevel(skill), amount); int value = change.apply(playerData.getSkillLevel(skill), amount);
playerData.setSkillLevel(skill, value); playerData.setSkillLevel(skill, value);
CommandVerbose.verbose(sender, CommandVerbose.CommandType.SKILL, ChatColor.GOLD + player.getName() + ChatColor.YELLOW CommandVerbose.verbose(sender, CommandVerbose.CommandType.SKILL, ChatColor.YELLOW + player.getName() + ChatColor.YELLOW
+ " is now level " + ChatColor.GOLD + value + ChatColor.YELLOW + " for " + skill.getName() + "."); + " is now level " + ChatColor.GOLD + value + ChatColor.YELLOW + " for " + skill.getName() + ".");
return CommandResult.SUCCESS; return CommandResult.SUCCESS;
} }
@ -100,31 +100,31 @@ public class SkillCommandTreeNode extends CommandTreeNode {
return CommandResult.THROW_USAGE; return CommandResult.THROW_USAGE;
Player player = Bukkit.getPlayer(args[3]); Player player = Bukkit.getPlayer(args[3]);
if (player == null) { if (player == null) {
sender.sendMessage(ChatColor.RED + "Could not find the player called " + args[3] + "."); sender.sendMessage(ChatColor.RED + "Could not find player called " + args[3] + ".");
return CommandResult.FAILURE; return CommandResult.FAILURE;
} }
PlayerData playerData = PlayerData.get(player); PlayerData playerData = PlayerData.get(player);
ClassSkill skill = playerData.getProfess().getSkill(args[4]); ClassSkill skill = playerData.getProfess().getSkill(args[4]);
if (skill == null) { if (skill == null) {
sender.sendMessage(ChatColor.RED + "The player's class doesn't have a skill called " + args[4] + "."); sender.sendMessage(ChatColor.RED + "Class doesn't have a skill called " + args[4] + ".");
return CommandResult.FAILURE; return CommandResult.FAILURE;
} }
if (lock) { if (lock) {
if (!playerData.hasUnlocked(skill)) { if (!playerData.hasUnlocked(skill)) {
CommandVerbose.verbose(sender, CommandVerbose.CommandType.SKILL, ChatColor.RED + "The skill " + skill.getSkill().getName() + " is already locked" + " for " + player.getName()); CommandVerbose.verbose(sender, CommandVerbose.CommandType.SKILL, ChatColor.RED + "Skill " + skill.getSkill().getName() + " already locked for " + player.getName());
return CommandResult.SUCCESS; return CommandResult.SUCCESS;
} }
playerData.lock(skill); playerData.lock(skill);
} else { } else {
if (playerData.hasUnlocked(skill)) { if (playerData.hasUnlocked(skill)) {
CommandVerbose.verbose(sender, CommandVerbose.CommandType.SKILL, ChatColor.RED + "The skill " + skill.getSkill().getName() + " is already unlocked" + " for " + player.getName()); CommandVerbose.verbose(sender, CommandVerbose.CommandType.SKILL, ChatColor.RED + "Skill " + skill.getSkill().getName() + " already unlocked for " + player.getName());
return CommandResult.SUCCESS; return CommandResult.SUCCESS;
} }
playerData.unlock(skill); playerData.unlock(skill);
} }
CommandVerbose.verbose(sender, CommandVerbose.CommandType.SKILL, ChatColor.GOLD + "The skill " + skill.getSkill().getName() + " is now " + (lock ? "locked" : "unlocked" + " for " + player.getName())); CommandVerbose.verbose(sender, CommandVerbose.CommandType.SKILL, ChatColor.YELLOW + "Skill " + ChatColor.GOLD + skill.getSkill().getName() + ChatColor.YELLOW + " now " + (lock ? "locked" : "unlocked") + " for " + ChatColor.GOLD + player.getName());
return CommandResult.SUCCESS; return CommandResult.SUCCESS;
} }
} }

View File

@ -4,6 +4,7 @@ import io.lumine.mythic.lib.command.api.CommandTreeNode;
import io.lumine.mythic.lib.command.api.Parameter; import io.lumine.mythic.lib.command.api.Parameter;
import net.Indyuce.mmocore.MMOCore; import net.Indyuce.mmocore.MMOCore;
import net.Indyuce.mmocore.api.player.PlayerData; import net.Indyuce.mmocore.api.player.PlayerData;
import net.Indyuce.mmocore.skill.binding.BoundSkillInfo;
import net.Indyuce.mmocore.skill.binding.SkillSlot; import net.Indyuce.mmocore.skill.binding.SkillSlot;
import net.Indyuce.mmocore.command.api.CommandVerbose; import net.Indyuce.mmocore.command.api.CommandVerbose;
import net.Indyuce.mmocore.skill.ClassSkill; import net.Indyuce.mmocore.skill.ClassSkill;
@ -54,7 +55,7 @@ public class SlotCommandTreeNode extends CommandTreeNode {
return CommandResult.FAILURE; return CommandResult.FAILURE;
} }
SkillSlot skillSlot = playerData.getProfess().getSkillSlot(slot); SkillSlot skillSlot = playerData.getProfess().getSkillSlot(slot);
if(skillSlot.isUnlockedByDefault()){ if (skillSlot.isUnlockedByDefault()) {
sender.sendMessage(ChatColor.RED + "You can't lock a skill that is unlocked by default."); sender.sendMessage(ChatColor.RED + "You can't lock a skill that is unlocked by default.");
return CommandResult.FAILURE; return CommandResult.FAILURE;
} }
@ -72,7 +73,7 @@ public class SlotCommandTreeNode extends CommandTreeNode {
} }
playerData.unlock(skillSlot); playerData.unlock(skillSlot);
} }
CommandVerbose.verbose(sender, CommandVerbose.CommandType.SKILL, ChatColor.GOLD + "The skill slot " + skillSlot.getName() + " is now " + (lock ? "locked" : "unlocked" + " for " + player.getName())); CommandVerbose.verbose(sender, CommandVerbose.CommandType.SKILL, ChatColor.YELLOW + "The skill slot " + skillSlot.getName() + " is now " + (lock ? "locked" : "unlocked" + " for " + player.getName()));
return CommandResult.SUCCESS; return CommandResult.SUCCESS;
} }
} }
@ -113,7 +114,7 @@ public class SlotCommandTreeNode extends CommandTreeNode {
} }
playerData.bindSkill(slot, skill); playerData.bindSkill(slot, skill);
CommandVerbose.verbose(sender, CommandVerbose.CommandType.SKILL, ChatColor.GOLD + "The skill " + skill.getSkill().getHandler().getId() + " is now bound to the slot " + slot); CommandVerbose.verbose(sender, CommandVerbose.CommandType.SKILL, ChatColor.YELLOW + "Skill " + ChatColor.GOLD + skill.getSkill().getHandler().getId() + ChatColor.YELLOW + " now bound to slot " + ChatColor.GOLD + slot);
return CommandResult.SUCCESS; return CommandResult.SUCCESS;
} }
} }
@ -143,11 +144,10 @@ public class SlotCommandTreeNode extends CommandTreeNode {
sender.sendMessage(ChatColor.RED + args[4] + " is not a valid number."); sender.sendMessage(ChatColor.RED + args[4] + " is not a valid number.");
return CommandResult.FAILURE; return CommandResult.FAILURE;
} }
String skill = playerData.hasSkillBound(slot) ? playerData.getBoundSkill(slot).getSkill().getHandler().getId() : "none"; final BoundSkillInfo found = playerData.unbindSkill(slot);
if (playerData.hasSkillBound(slot)) CommandVerbose.verbose(sender, CommandVerbose.CommandType.SKILL, ChatColor.YELLOW + (found != null ?
playerData.unbindSkill(slot); "Skill " + ChatColor.GOLD + found.getClassSkill().getSkill().getName() + ChatColor.YELLOW + " was taken off the slot " + ChatColor.GOLD + slot :
"Could not find skill at slot " + ChatColor.GOLD + slot));
CommandVerbose.verbose(sender, CommandVerbose.CommandType.SKILL, ChatColor.GOLD + "The skill " + skill + " has been unbounded from the slot " + slot);
return CommandResult.SUCCESS; return CommandResult.SUCCESS;
} }
} }

View File

@ -19,7 +19,7 @@ import java.util.*;
public class ClassSkill implements CooldownObject, Unlockable { public class ClassSkill implements CooldownObject, Unlockable {
private final RegisteredSkill skill; private final RegisteredSkill skill;
private final int unlockLevel, maxSkillLevel; private final int unlockLevel, maxSkillLevel;
private final boolean unlockedByDefault, needsBinding, upgradable; private final boolean unlockedByDefault, permanent, upgradable;
private final Map<String, LinearValue> parameters = new HashMap<>(); private final Map<String, LinearValue> parameters = new HashMap<>();
public ClassSkill(RegisteredSkill skill, int unlockLevel, int maxSkillLevel) { public ClassSkill(RegisteredSkill skill, int unlockLevel, int maxSkillLevel) {
@ -48,7 +48,7 @@ public class ClassSkill implements CooldownObject, Unlockable {
this.unlockLevel = unlockLevel; this.unlockLevel = unlockLevel;
this.maxSkillLevel = maxSkillLevel; this.maxSkillLevel = maxSkillLevel;
this.unlockedByDefault = unlockedByDefault; this.unlockedByDefault = unlockedByDefault;
this.needsBinding = needsBinding; this.permanent = !needsBinding && skill.getTrigger().isPassive();
this.upgradable = upgradable; this.upgradable = upgradable;
for (String param : skill.getHandler().getParameters()) for (String param : skill.getHandler().getParameters())
this.parameters.put(param, skill.getParameterInfo(param)); this.parameters.put(param, skill.getParameterInfo(param));
@ -59,7 +59,7 @@ public class ClassSkill implements CooldownObject, Unlockable {
unlockLevel = config.getInt("level"); unlockLevel = config.getInt("level");
maxSkillLevel = config.getInt("max-level"); maxSkillLevel = config.getInt("max-level");
unlockedByDefault = config.getBoolean("unlocked-by-default", true); unlockedByDefault = config.getBoolean("unlocked-by-default", true);
needsBinding = config.getBoolean("needs-bound", MMOCore.plugin.configManager.passiveSkillsNeedBinding); permanent = !config.getBoolean("needs-bound", MMOCore.plugin.configManager.passiveSkillsNeedBinding) && skill.getTrigger().isPassive();
upgradable = config.getBoolean("upgradable", true); upgradable = config.getBoolean("upgradable", true);
for (String param : skill.getHandler().getParameters()) { for (String param : skill.getHandler().getParameters()) {
LinearValue defaultValue = skill.getParameterInfo(param); LinearValue defaultValue = skill.getParameterInfo(param);
@ -93,8 +93,18 @@ public class ClassSkill implements CooldownObject, Unlockable {
return unlockedByDefault; return unlockedByDefault;
} }
@Deprecated
public boolean needsBound() { public boolean needsBound() {
return needsBinding; return !isPermanent();
}
/**
* @return Permanent skills are passive skills which do
* not have to be bound in order to apply their effects.
* Permanent skills can only be passive skills.
*/
public boolean isPermanent() {
return permanent;
} }
@Override @Override
@ -104,20 +114,22 @@ public class ClassSkill implements CooldownObject, Unlockable {
@Override @Override
public void whenLocked(PlayerData playerData) { public void whenLocked(PlayerData playerData) {
playerData.mapBoundSkills().forEach((slot, skill) -> {
if (skill.equalsIgnoreCase(getUnlockNamespacedKey().split(":")[1])) // Unbind the skill if necessary
new HashMap<>(playerData.getBoundSkills()).forEach((slot, bound) -> {
if (this.equals(bound.getClassSkill()))
playerData.unbindSkill(slot); playerData.unbindSkill(slot);
}); });
// Update the stats to remove the passive skill if it is locked // Update stats to flush permanent skill
if (!needsBinding && getSkill().getTrigger().isPassive()) if (isPermanent()) playerData.getStats().updateStats();
playerData.getStats().updateStats();
} }
@Override @Override
public void whenUnlocked(PlayerData playerData) { public void whenUnlocked(PlayerData playerData) {
if (!needsBinding && getSkill().getTrigger().isPassive())
playerData.getStats().updateStats(); // Update stats to register permanent skill
if (isPermanent()) playerData.getStats().updateStats();
} }
@ -138,7 +150,6 @@ public class ClassSkill implements CooldownObject, Unlockable {
parameters.put(parameter, linear); parameters.put(parameter, linear);
} }
/** /**
* Skill modifiers are now called parameters. * Skill modifiers are now called parameters.
*/ */
@ -190,10 +201,10 @@ public class ClassSkill implements CooldownObject, Unlockable {
* is called. It needs to be saved somewhere when trying to * is called. It needs to be saved somewhere when trying to
* unregister the passive skill from the skill map later on. * unregister the passive skill from the skill map later on.
*/ */
@NotNull
public PassiveSkill toPassive(PlayerData caster) { public PassiveSkill toPassive(PlayerData caster) {
Validate.isTrue(skill.getTrigger().isPassive(), "Skill is active"); Validate.isTrue(skill.getTrigger().isPassive(), "Skill is active");
//MMOCorePassiveSkillNotBound to identify passive skills that don't need to be bound return new PassiveSkill("MMOCore" + (permanent ? "Permanent" : "Passive") + "Skill", toCastable(caster), EquipmentSlot.OTHER, ModifierSource.OTHER);
return new PassiveSkill("MMOCorePassiveSkill" + (!needsBinding ? "NotBound" : ""), toCastable(caster), EquipmentSlot.OTHER, ModifierSource.OTHER);
} }
@Override @Override

View File

@ -33,7 +33,7 @@ public class BoundSkillInfo implements Closeable {
if (skillModifierTrigger.getTargetSkills().contains(classSkill.getSkill().getHandler())) if (skillModifierTrigger.getTargetSkills().contains(classSkill.getSkill().getHandler()))
skillModifierTrigger.apply(playerData, classSkill.getSkill().getHandler()); skillModifierTrigger.apply(playerData, classSkill.getSkill().getHandler());
if (classSkill.getSkill().getTrigger().isPassive() && classSkill.needsBound()) { if (classSkill.getSkill().getTrigger().isPassive() && !classSkill.isPermanent()) {
registered = classSkill.toPassive(playerData); registered = classSkill.toPassive(playerData);
registered.register(playerData.getMMOPlayerData()); registered.register(playerData.getMMOPlayerData());
} else registered = null; } else registered = null;
@ -64,7 +64,7 @@ public class BoundSkillInfo implements Closeable {
open = false; open = false;
// Unregister skill if passive // Unregister skill if passive
if (isPassive()&& classSkill.needsBound()) registered.unregister(playerData.getMMOPlayerData()); if (registered != null) registered.unregister(playerData.getMMOPlayerData());
// Remove skill buffs associated to the slot // Remove skill buffs associated to the slot
skillSlot.getSkillModifierTriggers().forEach(skillBuffTrigger -> skillBuffTrigger.remove(playerData, classSkill.getSkill().getHandler())); skillSlot.getSkillModifierTriggers().forEach(skillBuffTrigger -> skillBuffTrigger.remove(playerData, classSkill.getSkill().getHandler()));

View File

@ -1,7 +1,9 @@
package net.Indyuce.mmocore.skill.cast; package net.Indyuce.mmocore.skill.cast;
import io.lumine.mythic.lib.UtilityMethods;
import net.Indyuce.mmocore.MMOCore; import net.Indyuce.mmocore.MMOCore;
import net.Indyuce.mmocore.api.player.PlayerData; import net.Indyuce.mmocore.api.player.PlayerData;
import net.Indyuce.mmocore.skill.binding.BoundSkillInfo;
import org.apache.commons.lang.Validate; import org.apache.commons.lang.Validate;
import org.bukkit.Bukkit; import org.bukkit.Bukkit;
import org.bukkit.event.HandlerList; import org.bukkit.event.HandlerList;
@ -9,11 +11,20 @@ import org.bukkit.event.Listener;
import org.bukkit.scheduler.BukkitRunnable; import org.bukkit.scheduler.BukkitRunnable;
import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.NotNull;
import java.util.List;
import java.util.stream.Collectors;
public abstract class SkillCastingInstance extends BukkitRunnable implements Listener { public abstract class SkillCastingInstance extends BukkitRunnable implements Listener {
private final PlayerData caster; private final PlayerData caster;
private final SkillCastingHandler handler; private final SkillCastingHandler handler;
private final int runnablePeriod = 10; // Hard coded
private static final int RUNNABLE_PERIOD = 10;
/**
* This variable temporarily stores the active skills that the player
* can try to cast.
*/
private List<BoundSkillInfo> activeSkills;
private boolean open = true; private boolean open = true;
private int j, sinceLastActivity; private int j, sinceLastActivity;
@ -45,11 +56,18 @@ public abstract class SkillCastingInstance extends BukkitRunnable implements Lis
sinceLastActivity = 0; sinceLastActivity = 0;
} }
@NotNull
public List<BoundSkillInfo> getActiveSkills() {
if (activeSkills == null)
activeSkills = caster.getBoundSkills().values().stream().filter(bound -> !bound.isPassive()).collect(Collectors.toList());
return activeSkills;
}
private static final int PARTICLES_PER_TICK = 2; private static final int PARTICLES_PER_TICK = 2;
@Override @Override
public void run() { public void run() {
if (!caster.isOnline() || caster.getPlayer().isDead() || caster.getBoundSkills().isEmpty()) { if (UtilityMethods.isInvalidated(caster.getMMOPlayerData()) || !caster.hasActiveSkillBound()) {
caster.leaveSkillCasting(true); caster.leaveSkillCasting(true);
return; return;
} }
@ -67,7 +85,10 @@ public abstract class SkillCastingInstance extends BukkitRunnable implements Lis
} }
// Apply casting mode-specific effects // Apply casting mode-specific effects
if (j++ % runnablePeriod == 0) onTick(); if (j++ % RUNNABLE_PERIOD == 0) {
activeSkills = null;
onTick();
}
} }
public abstract void onTick(); public abstract void onTick();

View File

@ -74,7 +74,7 @@ public class KeyCombos extends SkillCastingHandler {
if (player.getGameMode() == GameMode.CREATIVE && !MMOCore.plugin.configManager.canCreativeCast) return; if (player.getGameMode() == GameMode.CREATIVE && !MMOCore.plugin.configManager.canCreativeCast) return;
// Don't start combos if no skills are bound // Don't start combos if no skills are bound
if (playerData.getBoundSkills().isEmpty()) return; if (!playerData.hasActiveSkillBound()) return;
// Start combo when there is an initializer key // Start combo when there is an initializer key
if (!event.getData().isCasting() && initializerKey != null) { if (!event.getData().isCasting() && initializerKey != null) {

View File

@ -10,6 +10,7 @@ import net.Indyuce.mmocore.api.SoundEvent;
import net.Indyuce.mmocore.api.event.PlayerKeyPressEvent; import net.Indyuce.mmocore.api.event.PlayerKeyPressEvent;
import net.Indyuce.mmocore.api.player.PlayerData; import net.Indyuce.mmocore.api.player.PlayerData;
import net.Indyuce.mmocore.skill.ClassSkill; import net.Indyuce.mmocore.skill.ClassSkill;
import net.Indyuce.mmocore.skill.binding.BoundSkillInfo;
import net.Indyuce.mmocore.skill.cast.PlayerKey; import net.Indyuce.mmocore.skill.cast.PlayerKey;
import net.Indyuce.mmocore.skill.cast.SkillCastingHandler; import net.Indyuce.mmocore.skill.cast.SkillCastingHandler;
import net.Indyuce.mmocore.skill.cast.SkillCastingInstance; import net.Indyuce.mmocore.skill.cast.SkillCastingInstance;
@ -57,7 +58,10 @@ public class SkillBar extends SkillCastingHandler {
// Enter spell casting // Enter spell casting
final PlayerData playerData = event.getData(); final PlayerData playerData = event.getData();
if (player.getGameMode() != GameMode.SPECTATOR && (MMOCore.plugin.configManager.canCreativeCast || player.getGameMode() != GameMode.CREATIVE) && !playerData.isCasting() && !playerData.getBoundSkills().isEmpty()) if (player.getGameMode() != GameMode.SPECTATOR
&& (MMOCore.plugin.configManager.canCreativeCast || player.getGameMode() != GameMode.CREATIVE)
&& !playerData.isCasting()
&& playerData.hasActiveSkillBound())
if (playerData.setSkillCasting()) if (playerData.setSkillCasting())
MMOCore.plugin.soundManager.getSound(SoundEvent.SPELL_CAST_BEGIN).playTo(player); MMOCore.plugin.soundManager.getSound(SoundEvent.SPELL_CAST_BEGIN).playTo(player);
} }
@ -92,16 +96,17 @@ public class SkillBar extends SkillCastingHandler {
event.setCancelled(true); event.setCancelled(true);
refreshTimeOut(); refreshTimeOut();
final int slot = event.getNewSlot() + 1 + (event.getNewSlot() >= player.getInventory().getHeldItemSlot() ? -1 : 0); final int activeSlot = event.getNewSlot() + (event.getNewSlot() >= player.getInventory().getHeldItemSlot() ? -1 : 0);
/* /*
* The event is called again soon after the first since when * The event is called again soon after the first since when
* cancelling the first one, the player held item slot must go back * cancelling the first one, the player held item slot must go back
* to the previous one. * to the previous one.
*/ */
if (slot >= 1 && getCaster().hasSkillBound(slot)) { if (activeSlot < getActiveSkills().size()) {
final ClassSkill classSkill = getActiveSkills().get(activeSlot).getClassSkill();
final PlayerMetadata caster = getCaster().getMMOPlayerData().getStatMap().cache(EquipmentSlot.MAIN_HAND); final PlayerMetadata caster = getCaster().getMMOPlayerData().getStatMap().cache(EquipmentSlot.MAIN_HAND);
getCaster().getBoundSkill(slot).toCastable(getCaster()).cast(new TriggerMetadata(caster, null, null)); classSkill.toCastable(getCaster()).cast(new TriggerMetadata(caster, null, null));
} }
} }
@ -121,18 +126,22 @@ public class SkillBar extends SkillCastingHandler {
} }
} }
@NotNull
private String getFormat(PlayerData data) { private String getFormat(PlayerData data) {
final StringBuilder str = new StringBuilder(); final StringBuilder str = new StringBuilder();
if (!data.isOnline()) return str.toString(); if (!data.isOnline()) return str.toString();
for (int slot : data.mapBoundSkills().keySet()) {
final ClassSkill skill = data.getBoundSkill(slot);
if (skill.getSkill().getTrigger().isPassive()) continue;
str.append(str.isEmpty() ? "" : split).append((onCooldown(data, skill) ? onCooldown.replace("{cooldown}", int slot = 1;
String.valueOf(data.getCooldownMap().getInfo(skill).getRemaining() / 1000)) : noMana(data, skill) ? noMana : (noStamina( for (BoundSkillInfo active : getActiveSkills()) {
data, skill) ? noStamina : ready)).replace("{index}", final ClassSkill skill = active.getClassSkill();
String.valueOf(slot + (data.getPlayer().getInventory().getHeldItemSlot() < slot ? 1 : 0)))
.replace("{skill}", data.getBoundSkill(slot).getSkill().getName())); str.append(str.isEmpty() ? "" : split).append(
(onCooldown(data, skill) ? onCooldown.replace("{cooldown}",
String.valueOf(data.getCooldownMap().getInfo(skill).getRemaining() / 1000)) :
noMana(data, skill) ? noMana : (noStamina(data, skill) ? noStamina : ready))
.replace("{index}", String.valueOf(slot + (data.getPlayer().getInventory().getHeldItemSlot() < slot ? 1 : 0)))
.replace("{skill}", skill.getSkill().getName()));
slot++;
} }
return MMOCore.plugin.placeholderParser.parse(data.getPlayer(), str.toString()); return MMOCore.plugin.placeholderParser.parse(data.getPlayer(), str.toString());
} }

View File

@ -83,7 +83,7 @@ public class SkillScroller extends SkillCastingHandler {
} }
// Check if there are skills bound // Check if there are skills bound
if (playerData.getBoundSkills().isEmpty()) return; if (!playerData.hasActiveSkillBound()) return;
// Cancel event if necessary // Cancel event if necessary
if (event.getPressed().shouldCancelEvent()) event.setCancelled(true); if (event.getPressed().shouldCancelEvent()) event.setCancelled(true);
@ -120,7 +120,7 @@ public class SkillScroller extends SkillCastingHandler {
} }
public ClassSkill getSelected() { public ClassSkill getSelected() {
return getCaster().getBoundSkill(index + 1); return getActiveSkills().get(index).getClassSkill();
} }
@EventHandler @EventHandler
@ -128,7 +128,7 @@ public class SkillScroller extends SkillCastingHandler {
if (!event.getPlayer().equals(getCaster().getPlayer())) return; if (!event.getPlayer().equals(getCaster().getPlayer())) return;
PlayerData playerData = PlayerData.get(event.getPlayer()); PlayerData playerData = PlayerData.get(event.getPlayer());
if (playerData.getBoundSkills().isEmpty()) { if (!playerData.hasActiveSkillBound()) {
playerData.leaveSkillCasting(true); playerData.leaveSkillCasting(true);
return; return;
} }
@ -139,9 +139,9 @@ public class SkillScroller extends SkillCastingHandler {
final int dist1 = 9 + current - previous, dist2 = current - previous, dist3 = current - previous - 9; final int dist1 = 9 + current - previous, dist2 = current - previous, dist3 = current - previous - 9;
final int change = Math.abs(dist1) < Math.abs(dist2) ? (Math.abs(dist1) < Math.abs(dist3) ? dist1 : dist3) : (Math.abs(dist3) < Math.abs(dist2) ? dist3 : dist2); final 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 // Scroll through items
final CustomSkillCastingInstance casting = (CustomSkillCastingInstance) playerData.getSkillCasting(); final CustomSkillCastingInstance casting = (CustomSkillCastingInstance) playerData.getSkillCasting();
casting.index = mod(casting.index + change, playerData.getBoundSkills().size()); casting.index = mod(casting.index + change, getActiveSkills().size());
casting.onTick(); casting.onTick();
casting.refreshTimeOut(); casting.refreshTimeOut();