Factorized key combos

This commit is contained in:
Indyuce 2022-10-16 22:57:27 +02:00
parent fb2cffe57f
commit 3e71ee7d66
3 changed files with 119 additions and 118 deletions

View File

@ -31,10 +31,8 @@ import net.Indyuce.mmocore.loot.chest.particle.CastingParticle;
import net.Indyuce.mmocore.player.stats.StatInfo; import net.Indyuce.mmocore.player.stats.StatInfo;
import net.Indyuce.mmocore.skill.ClassSkill; import net.Indyuce.mmocore.skill.ClassSkill;
import net.Indyuce.mmocore.skill.RegisteredSkill; import net.Indyuce.mmocore.skill.RegisteredSkill;
import net.Indyuce.mmocore.skill.cast.KeyCombo; import net.Indyuce.mmocore.skill.cast.ComboMap;
import net.Indyuce.mmocore.skill.cast.PlayerKey;
import net.md_5.bungee.api.ChatColor; import net.md_5.bungee.api.ChatColor;
import org.apache.commons.lang.Validate;
import org.bukkit.Location; import org.bukkit.Location;
import org.bukkit.Material; import org.bukkit.Material;
import org.bukkit.Particle; import org.bukkit.Particle;
@ -73,17 +71,11 @@ public class PlayerClass extends PostLoadObject implements ExperienceObject {
private final Map<String, LinearValue> stats = new HashMap<>(); private final Map<String, LinearValue> stats = new HashMap<>();
private final Map<String, ClassSkill> skills = new LinkedHashMap<>(); private final Map<String, ClassSkill> skills = new LinkedHashMap<>();
private final List<Subclass> subclasses = new ArrayList<>(); private final List<Subclass> subclasses = new ArrayList<>();
// If the class redefines its own key combos.
private final Map<KeyCombo, Integer> combos = new HashMap<>();
private final Set<PlayerKey> firstComboKeys= new HashSet<>();
private int longestCombo;
private final Map<PlayerResource, ResourceRegeneration> resourceHandlers = new HashMap<>(); private final Map<PlayerResource, ResourceRegeneration> resourceHandlers = new HashMap<>();
@Nullable
private final ComboMap comboMap;
@Deprecated @Deprecated
private final Map<String, EventTrigger> eventTriggers = new HashMap<>(); private final Map<String, EventTrigger> eventTriggers = new HashMap<>();
@ -147,22 +139,14 @@ public class PlayerClass extends PostLoadObject implements ExperienceObject {
} }
// Load different combos // Load different combos
ComboMap comboMap = null;
if (config.contains("key-combos")) if (config.contains("key-combos"))
for (String key : config.getConfigurationSection("key-combos").getKeys(false)) try {
try { comboMap = new ComboMap(config.getConfigurationSection("key-combos"));
int spellSlot = Integer.valueOf(key); } catch (Exception exception) {
Validate.isTrue(spellSlot >= 0, "Spell slot must be at least 0"); MMOCore.plugin.getLogger().log(Level.WARNING, "Could not load combo map from class '" + id + "': " + exception.getMessage());
Validate.isTrue(!combos.values().contains(spellSlot), "There is already a key combo with the same skill slot"); }
KeyCombo combo = new KeyCombo(); this.comboMap = comboMap;
for (String str : config.getStringList("key-combos." + key))
combo.registerKey(PlayerKey.valueOf(UtilityMethods.enumName(str)));
combos.put(combo, spellSlot);
firstComboKeys.add(combo.getAt(0));
longestCombo = Math.max(longestCombo, combo.countKeys());
} catch (RuntimeException exception) {
MMOCore.plugin.getLogger().log(Level.WARNING, "Could not load key combo '" + key + "': " + exception.getMessage());
}
if (config.contains("triggers")) if (config.contains("triggers"))
for (String key : config.getConfigurationSection("triggers").getKeys(false)) for (String key : config.getConfigurationSection("triggers").getKeys(false))
@ -252,6 +236,7 @@ public class PlayerClass extends PostLoadObject implements ExperienceObject {
displayOrder = 0; displayOrder = 0;
expCurve = ExpCurve.DEFAULT; expCurve = ExpCurve.DEFAULT;
expTable = null; expTable = null;
comboMap = null;
castParticle = new CastingParticle(Particle.SPELL_INSTANT); castParticle = new CastingParticle(Particle.SPELL_INSTANT);
actionBarFormat = ""; actionBarFormat = "";
this.icon = new ItemStack(material); this.icon = new ItemStack(material);
@ -446,16 +431,8 @@ public class PlayerClass extends PostLoadObject implements ExperienceObject {
} }
@Nullable @Nullable
public Map<KeyCombo, Integer> getKeyCombos() { public ComboMap getComboMap() {
return combos; return comboMap;
}
public Set<PlayerKey> getFirstComboKeys() {
return firstComboKeys;
}
public int getLongestCombo() {
return longestCombo;
} }
@NotNull @NotNull

View File

@ -0,0 +1,60 @@
package net.Indyuce.mmocore.skill.cast;
import io.lumine.mythic.lib.UtilityMethods;
import org.apache.commons.lang.Validate;
import org.bukkit.configuration.ConfigurationSection;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Map;
import java.util.Set;
public class ComboMap {
/**
* Using instances of KeyCombo as keys work because {@link KeyCombo}
* has a working implementation for the hash code method
*/
private final Map<KeyCombo, Integer> combos = new HashMap<>();
/**
* All the keys that are at the start of a combo.
*/
private final Set<PlayerKey> firstKeys = new HashSet<>();
private final int longestCombo;
public ComboMap(ConfigurationSection config) {
int currentLongestCombo = 0;
for (String key : config.getKeys(false))
try {
final 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);
firstKeys.add(combo.getAt(0));
currentLongestCombo = Math.max(currentLongestCombo, combo.countKeys());
} catch (RuntimeException exception) {
throw new RuntimeException("Could not loading key combo '" + key + "': " + exception.getMessage());
}
this.longestCombo = currentLongestCombo;
}
public Map<KeyCombo, Integer> getCombos() {
return combos;
}
public int getLongest() {
return longestCombo;
}
public boolean isComboStart(PlayerKey key) {
return firstKeys.contains(key);
}
}

View File

@ -11,10 +11,10 @@ import net.Indyuce.mmocore.api.SoundObject;
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.gui.api.item.Placeholders; import net.Indyuce.mmocore.gui.api.item.Placeholders;
import net.Indyuce.mmocore.skill.cast.ComboMap;
import net.Indyuce.mmocore.skill.cast.KeyCombo; import net.Indyuce.mmocore.skill.cast.KeyCombo;
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 org.apache.commons.lang.Validate;
import org.bukkit.configuration.ConfigurationSection; import org.bukkit.configuration.ConfigurationSection;
import org.bukkit.entity.Player; import org.bukkit.entity.Player;
import org.bukkit.event.EventHandler; import org.bukkit.event.EventHandler;
@ -22,30 +22,18 @@ import org.bukkit.event.Listener;
import javax.annotation.Nullable; import javax.annotation.Nullable;
import java.util.*; import java.util.*;
import java.util.logging.Level;
public class KeyCombos implements Listener { public class KeyCombos implements Listener {
/** private final ComboMap comboMap;
* Using instances of KeyCombo as keys work because
* {@link KeyCombo} has a working implementation for the
* hash code method
*/
private final Map<KeyCombo, Integer> combos = new HashMap<>();
/**
* All the keys that are at the start of a combo.
*/
private final Set<PlayerKey> firstComboKeys = new HashSet<>();
/** /**
* Key players need to press to start a combo. If it's set to * Key players need to press to start a combo. If it's set to
* null then the player can press any key which starts a combo. * null then the player can press any key which starts a combo.
* These "starting keys" are saved in {@link #firstComboKeys} * These "starting keys" are saved in the combo map
*/ */
@Nullable @Nullable
private final PlayerKey initializerKey; private final PlayerKey initializerKey;
private final int longestCombo;
/** /**
* Handles the display of the action bar when casting a skill. * Handles the display of the action bar when casting a skill.
@ -58,28 +46,7 @@ public class KeyCombos implements Listener {
private final SoundObject beginComboSound, comboClickSound, failComboSound; private final SoundObject beginComboSound, comboClickSound, failComboSound;
public KeyCombos(ConfigurationSection config) { public KeyCombos(ConfigurationSection config) {
comboMap = new ComboMap(config.getConfigurationSection("combos"));
// Load different combos
int currentLongestCombo = 0;
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);
firstComboKeys.add(combo.getAt(0));
currentLongestCombo = Math.max(currentLongestCombo, combo.countKeys());
} catch (RuntimeException exception) {
MMOCore.plugin.getLogger().log(Level.WARNING, "Could not load key combo '" + key + "': " + exception.getMessage());
}
this.longestCombo = currentLongestCombo;
// Load player key names
actionBarOptions = config.contains("action-bar") ? new ActionBarOptions(config.getConfigurationSection("action-bar")) : null; actionBarOptions = config.contains("action-bar") ? new ActionBarOptions(config.getConfigurationSection("action-bar")) : null;
// Load sounds // Load sounds
@ -97,48 +64,55 @@ public class KeyCombos implements Listener {
PlayerData playerData = event.getData(); PlayerData playerData = event.getData();
Player player = playerData.getPlayer(); Player player = playerData.getPlayer();
if (!event.getData().isCasting()) { // Start combo when there is an initializer key
if (initializerKey != null) { if (!event.getData().isCasting() && initializerKey != null) {
if (event.getPressed() == initializerKey) { if (event.getPressed() == initializerKey) {
// Start combo // Cancel event if necessary
playerData.setSkillCasting(new CustomSkillCastingHandler(playerData)); if (event.getPressed().shouldCancelEvent())
if (beginComboSound != null) event.setCancelled(true);
beginComboSound.playTo(player);
}
return;
} else {
Set<PlayerKey> firstKeys = playerData.getProfess().getFirstComboKeys().isEmpty() ? firstComboKeys : playerData.getProfess().getFirstComboKeys();
if (firstKeys.contains(event.getPressed())) {
// Always cancel drop event // Start combo
if (event.getPressed().equals(PlayerKey.DROP)) playerData.setSkillCasting(new CustomSkillCastingHandler(playerData));
event.setCancelled(true); if (beginComboSound != null)
beginComboSound.playTo(player);
}
return;
}
// Start combo CustomSkillCastingHandler casting = null;
CustomSkillCastingHandler casting = new CustomSkillCastingHandler(playerData);
playerData.setSkillCasting(casting); // Player is already casting
casting.current.registerKey(event.getPressed()); if (event.getData().isCasting())
if (beginComboSound != null) casting = (CustomSkillCastingHandler) playerData.getSkillCasting();
beginComboSound.playTo(player);
} // Start combo when there is NO initializer key
return; else {
final ComboMap comboMap = Objects.requireNonNullElse(playerData.getProfess().getComboMap(), this.comboMap);
if (comboMap.isComboStart(event.getPressed())) {
casting = new CustomSkillCastingHandler(playerData);
playerData.setSkillCasting(casting);
if (beginComboSound != null)
beginComboSound.playTo(player);
} }
} }
if (casting == null)
return;
// Adding pressed key // Adding pressed key
CustomSkillCastingHandler casting = (CustomSkillCastingHandler) playerData.getSkillCasting();
casting.current.registerKey(event.getPressed()); casting.current.registerKey(event.getPressed());
casting.onTick(); casting.onTick();
if (comboClickSound != null) if (comboClickSound != null)
comboClickSound.playTo(player); comboClickSound.playTo(player);
// Always cancel event // Cancel event if necessary
event.setCancelled(true); if (event.getPressed().shouldCancelEvent())
event.setCancelled(true);
// Hash current combo and check // Hash current combo and check
if (casting.classCombos.containsKey(casting.current)) { if (casting.combos.getCombos().containsKey(casting.current)) {
int spellSlot = casting.classCombos.get(casting.current) - 1; final int spellSlot = casting.combos.getCombos().get(casting.current) - 1;
playerData.leaveCastingMode(); playerData.leaveCastingMode();
// Cast spell // Cast spell
@ -150,7 +124,7 @@ public class KeyCombos implements Listener {
} }
// Check if current combo is too large // Check if current combo is too large
if (casting.current.countKeys() >= casting.classLongestCombo) { if (casting.current.countKeys() >= casting.combos.getLongest()) {
playerData.leaveCastingMode(); playerData.leaveCastingMode();
if (failComboSound != null) if (failComboSound != null)
failComboSound.playTo(player); failComboSound.playTo(player);
@ -181,25 +155,17 @@ public class KeyCombos implements Listener {
event.setCancelled(true); event.setCancelled(true);
} }
/** /**
* Loads the player current combos & the combos applicable to the player (combos defined in its class or the default combos of the config.yml) * Loads the player current combos & the combos applicable to the player (combos defined in its class or the default combos of the config.yml)
*/ */
private class CustomSkillCastingHandler extends SkillCastingHandler { private class CustomSkillCastingHandler extends SkillCastingHandler {
private final KeyCombo current = new KeyCombo(); private final KeyCombo current = new KeyCombo();
//Combos used: default combos from the config or the combos defined in the player class. private final ComboMap combos;
private final Map<KeyCombo, Integer> classCombos;
private int classLongestCombo;
CustomSkillCastingHandler(PlayerData caster) { CustomSkillCastingHandler(PlayerData caster) {
super(caster, 10); super(caster, 10);
if (!caster.getProfess().getKeyCombos().isEmpty()) {
classCombos = caster.getProfess().getKeyCombos(); combos = Objects.requireNonNullElse(caster.getProfess().getComboMap(), comboMap);
classLongestCombo = caster.getProfess().getLongestCombo();
} else {
classCombos = combos;
classLongestCombo = longestCombo;
}
} }
@Override @Override
@ -210,10 +176,8 @@ public class KeyCombos implements Listener {
else else
getCaster().displayActionBar(actionBarOptions.format(this)); getCaster().displayActionBar(actionBarOptions.format(this));
} }
} }
private class ActionBarOptions { private class ActionBarOptions {
private final String separator, noKey, prefix, suffix; private final String separator, noKey, prefix, suffix;
@ -229,7 +193,7 @@ public class KeyCombos implements Listener {
this.suffix = config.contains("suffix") ? config.getString("suffix") : ""; this.suffix = config.contains("suffix") ? config.getString("suffix") : "";
this.separator = Objects.requireNonNull(config.getString("separator"), "Could not find action bar option 'separator'"); 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'"); this.noKey = Objects.requireNonNull(config.getString("no-key"), "Could not find action bar option 'no-key'");
this.isSubtitle = config.getBoolean("" + "", false); this.isSubtitle = config.getBoolean("is-subtitle", false);
for (PlayerKey key : PlayerKey.values()) for (PlayerKey key : PlayerKey.values())
keyNames.put(key, Objects.requireNonNull(config.getString("key-name." + key.name()), "Could not find translation for key " + key.name())); keyNames.put(key, Objects.requireNonNull(config.getString("key-name." + key.name()), "Could not find translation for key " + key.name()));
} }
@ -246,7 +210,7 @@ public class KeyCombos implements Listener {
builder.append(separator + keyNames.get(casting.current.getAt(j))); builder.append(separator + keyNames.get(casting.current.getAt(j)));
// All remaining // All remaining
for (; j < casting.classLongestCombo; j++) for (; j < casting.combos.getLongest(); j++)
builder.append(separator + noKey); builder.append(separator + noKey);
builder.append(suffix); builder.append(suffix);