!new skill casting methods

This commit is contained in:
Indyuce 2022-01-16 18:07:57 +01:00
parent 156a7c5b56
commit 7d515e99dd
10 changed files with 557 additions and 73 deletions

View File

@ -125,7 +125,7 @@
<dependency>
<groupId>io.lumine</groupId>
<artifactId>MythicLib-dist</artifactId>
<version>1.3</version>
<version>1.3-R17</version>
<scope>provided</scope>
</dependency>

View File

@ -27,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;
@ -65,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;
@ -79,7 +80,6 @@ public class PlayerData extends OfflinePlayerData implements Closable {
// NON-FINAL player data stuff made public to facilitate field change
public int skillGuiDisplayOffset;
public Object skillCasting;
public boolean noCooldown;
public CombatRunnable combat;
@ -676,6 +676,22 @@ public class PlayerData extends OfflinePlayerData implements Closable {
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;
@ -809,7 +825,7 @@ public class PlayerData extends OfflinePlayerData implements Closable {
* 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())

View File

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

View File

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

View File

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

View File

@ -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.
* <p>
* 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<ConfigurationSection, Listener> listenerLoader;
CastingMethod(Function<ConfigurationSection, Listener> listenerLoader) {
SkillCastingMode(Function<ConfigurationSection, Listener> listenerLoader) {
this.listenerLoader = listenerLoader;
}
public Listener loadFromConfig(ConfigurationSection config) {
return listenerLoader.apply(config);
}
}

View File

@ -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<KeyCombo, Integer> 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<Integer, KeyCombo> 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<PlayerKey, String> 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);
}
}
}

View File

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

View File

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

View File

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