From 0532aafa83e92c0df6b20fe3b072b51f7b185959 Mon Sep 17 00:00:00 2001 From: Sollace Date: Fri, 31 Dec 2021 15:31:23 +0200 Subject: [PATCH] Added tap, double-tap, and triple-tap gestures --- .../unicopia/ability/Ability.java | 12 ++- .../unicopia/ability/AbilityDispatcher.java | 6 +- .../unicopia/ability/ActivationType.java | 18 ++++ .../unicopia/client/KeyBindingsHandler.java | 91 +++++++++++++++---- .../unicopia/client/gui/ManaRingSlot.java | 6 +- .../unicopia/client/gui/Slot.java | 5 +- .../unicopia/client/gui/UHud.java | 19 +++- 7 files changed, 126 insertions(+), 31 deletions(-) create mode 100644 src/main/java/com/minelittlepony/unicopia/ability/ActivationType.java diff --git a/src/main/java/com/minelittlepony/unicopia/ability/Ability.java b/src/main/java/com/minelittlepony/unicopia/ability/Ability.java index 8ac5e2e6..cb875c77 100644 --- a/src/main/java/com/minelittlepony/unicopia/ability/Ability.java +++ b/src/main/java/com/minelittlepony/unicopia/ability/Ability.java @@ -12,7 +12,6 @@ import net.minecraft.util.Identifier; import net.minecraft.world.World; public interface Ability { - /** * The amount of energy this ability is expected to cost if the player were to cast it. */ @@ -28,6 +27,17 @@ public interface Ability { */ int getCooldownTime(Pony player); + /** + * Called on the client when an ability is about to be triggered. + *

+ * Use this method to respond to quick-time events, like short taps or double-taps. + *

+ * @return True if the event has been handled. + */ + default boolean onQuickAction(Pony player, ActivationType type) { + return false; + } + /** * Called to check preconditions for activating the ability. * diff --git a/src/main/java/com/minelittlepony/unicopia/ability/AbilityDispatcher.java b/src/main/java/com/minelittlepony/unicopia/ability/AbilityDispatcher.java index 46418532..cf2e51cd 100644 --- a/src/main/java/com/minelittlepony/unicopia/ability/AbilityDispatcher.java +++ b/src/main/java/com/minelittlepony/unicopia/ability/AbilityDispatcher.java @@ -36,11 +36,13 @@ public class AbilityDispatcher implements Tickable, NbtSerialisable { this.player = player; } - public void clear(AbilitySlot slot) { + public void clear(AbilitySlot slot, ActivationType pressType, long page) { Stat stat = getStat(slot); if (stat.canSwitchStates()) { - stat.setActiveAbility(null); + if (pressType == ActivationType.NONE || stat.getAbility(page).filter(ability -> ability.onQuickAction(player, pressType)).isEmpty()) { + stat.setActiveAbility(null); + } } } diff --git a/src/main/java/com/minelittlepony/unicopia/ability/ActivationType.java b/src/main/java/com/minelittlepony/unicopia/ability/ActivationType.java new file mode 100644 index 00000000..57003e06 --- /dev/null +++ b/src/main/java/com/minelittlepony/unicopia/ability/ActivationType.java @@ -0,0 +1,18 @@ +package com.minelittlepony.unicopia.ability; + +public enum ActivationType { + NONE, + TAP, + DOUBLE_TAP, + TRIPLE_TAP; + + private static final ActivationType[] VALUES = values(); + + public ActivationType getNext() { + return VALUES[Math.min(VALUES.length - 1, ordinal() + 1)]; + } + + public int getTapCount() { + return ordinal(); + } +} \ No newline at end of file diff --git a/src/main/java/com/minelittlepony/unicopia/client/KeyBindingsHandler.java b/src/main/java/com/minelittlepony/unicopia/client/KeyBindingsHandler.java index bdb8eb91..b3392584 100644 --- a/src/main/java/com/minelittlepony/unicopia/client/KeyBindingsHandler.java +++ b/src/main/java/com/minelittlepony/unicopia/client/KeyBindingsHandler.java @@ -10,6 +10,7 @@ import org.lwjgl.glfw.GLFW; import com.minelittlepony.unicopia.ability.Ability; import com.minelittlepony.unicopia.ability.AbilityDispatcher; import com.minelittlepony.unicopia.ability.AbilitySlot; +import com.minelittlepony.unicopia.ability.ActivationType; import com.minelittlepony.unicopia.client.gui.UHud; import com.minelittlepony.unicopia.entity.player.Pony; @@ -18,6 +19,7 @@ import net.minecraft.client.MinecraftClient; import net.minecraft.client.option.KeyBinding; import net.minecraft.client.sound.PositionedSoundInstance; import net.minecraft.sound.SoundEvents; +import net.minecraft.text.Text; import net.minecraft.text.TranslatableText; import net.minecraft.util.math.MathHelper; @@ -28,11 +30,11 @@ public class KeyBindingsHandler { static void bootstrap() {} - private final Map keys = new HashMap<>(); - private final Map reverse = new HashMap<>(); + private final Map keys = new HashMap<>(); + private final Map reverse = new HashMap<>(); - private final KeyBinding pageDown = register(GLFW.GLFW_KEY_PAGE_DOWN, "hud_page_dn"); - private final KeyBinding pageUp = register(GLFW.GLFW_KEY_PAGE_UP, "hud_page_up"); + private final Binding pageDown = register(GLFW.GLFW_KEY_PAGE_DOWN, "hud_page_dn"); + private final Binding pageUp = register(GLFW.GLFW_KEY_PAGE_UP, "hud_page_up"); public long page = 0; @@ -44,18 +46,18 @@ public class KeyBindingsHandler { addKeybind(GLFW.GLFW_KEY_V, AbilitySlot.TERTIARY); } - public KeyBinding getBinding(AbilitySlot slot) { + public Binding getBinding(AbilitySlot slot) { return reverse.get(slot); } public void addKeybind(int code, AbilitySlot slot) { - KeyBinding binding = register(code, slot.name().toLowerCase()); + Binding binding = register(code, slot.name().toLowerCase()); reverse.put(slot, binding); keys.put(binding, slot); } - static KeyBinding register(int code, String name) { - return KeyBindingHelper.registerKeyBinding(new KeyBinding("key.unicopia." + name, code, KEY_CATEGORY)); + Binding register(int code, String name) { + return new Binding(KeyBindingHelper.registerKeyBinding(new KeyBinding("key.unicopia." + name, code, KEY_CATEGORY))); } public void tick(MinecraftClient client) { @@ -69,23 +71,29 @@ public class KeyBindingsHandler { page = MathHelper.clamp(page, 0, maxPage); - if (page > 0 && checkPressed(pageDown) == PressedState.PRESSED) { + if (page > 0 && pageDown.getState() == PressedState.PRESSED) { changePage(client, maxPage, -1); - } else if (page < maxPage && checkPressed(pageUp) == PressedState.PRESSED) { + } else if (page < maxPage && pageUp.getState() == PressedState.PRESSED) { changePage(client, maxPage, 1); } else { - for (KeyBinding i : keys.keySet()) { + for (Binding i : keys.keySet()) { AbilitySlot slot = keys.get(i); if (slot == AbilitySlot.PRIMARY && client.options.keySneak.isPressed()) { slot = AbilitySlot.PASSIVE; } - PressedState state = checkPressed(i); + PressedState state = i.getState(); + if (state != PressedState.UNCHANGED) { if (state == PressedState.PRESSED) { abilities.activate(slot, page).map(Ability::getName).ifPresent(UHud.INSTANCE::setMessage); } else { - abilities.clear(slot); + abilities.clear(slot, ActivationType.NONE, page); + } + } else { + ActivationType type = i.getType(); + if (type != ActivationType.NONE) { + abilities.clear(slot, type, page); } } } @@ -98,19 +106,62 @@ public class KeyBindingsHandler { UHud.INSTANCE.setMessage(new TranslatableText("gui.unicopia.page_num", page + 1, max + 1)); } - private PressedState checkPressed(KeyBinding binding) { - if (binding.isPressed()) { - return pressed.add(binding) ? PressedState.PRESSED : PressedState.UNCHANGED; - } else if (pressed.remove(binding)) { - return PressedState.UNPRESSED; + public class Binding { + private final KeyBinding binding; + + private long nextPhaseTime; + + private ActivationType type = ActivationType.NONE; + + Binding(KeyBinding binding) { + this.binding = binding; } - return PressedState.UNCHANGED; + public Text getLabel() { + return binding.getBoundKeyLocalizedText(); + } + + public PressedState getState() { + PressedState state = getNewState(); + + long now = System.currentTimeMillis(); + + if (state == PressedState.PRESSED) { + nextPhaseTime = now + 200; + } + + if (state == PressedState.RELEASED && now < nextPhaseTime + 10) { + nextPhaseTime = now + 200; + type = type.getNext(); + } + + return state; + } + + public ActivationType getType() { + long now = System.currentTimeMillis(); + if (type != ActivationType.NONE && now > nextPhaseTime - 70) { + ActivationType t = type; + type = ActivationType.NONE; + return t; + } + return ActivationType.NONE; + } + + private PressedState getNewState() { + if (binding.isPressed()) { + return pressed.add(binding) ? PressedState.PRESSED : PressedState.UNCHANGED; + } else if (pressed.remove(binding)) { + return PressedState.RELEASED; + } + + return PressedState.UNCHANGED; + } } enum PressedState { UNCHANGED, PRESSED, - UNPRESSED + RELEASED } } diff --git a/src/main/java/com/minelittlepony/unicopia/client/gui/ManaRingSlot.java b/src/main/java/com/minelittlepony/unicopia/client/gui/ManaRingSlot.java index 5e446571..79daf181 100644 --- a/src/main/java/com/minelittlepony/unicopia/client/gui/ManaRingSlot.java +++ b/src/main/java/com/minelittlepony/unicopia/client/gui/ManaRingSlot.java @@ -10,6 +10,7 @@ import com.mojang.blaze3d.systems.RenderSystem; import net.minecraft.client.render.BufferBuilder; import net.minecraft.client.render.BufferRenderer; +import net.minecraft.client.render.GameRenderer; import net.minecraft.client.render.Tessellator; import net.minecraft.client.render.VertexFormat; import net.minecraft.client.render.VertexFormats; @@ -106,11 +107,14 @@ class ManaRingSlot extends Slot { final double maxAngle = MathHelper.clamp(startAngle + arcAngle, 0, TWO_PI - increment); - BufferBuilder bufferBuilder = Tessellator.getInstance().getBuffer(); + RenderSystem.setShaderColor(1, 1, 1, 1); + RenderSystem.setShader(GameRenderer::getPositionColorShader); RenderSystem.enableBlend(); RenderSystem.disableTexture(); RenderSystem.defaultBlendFunc(); + BufferBuilder bufferBuilder = Tessellator.getInstance().getBuffer(); + bufferBuilder.begin(VertexFormat.DrawMode.QUADS, VertexFormats.POSITION_COLOR); Matrix4f model = matrices.peek().getPositionMatrix(); diff --git a/src/main/java/com/minelittlepony/unicopia/client/gui/Slot.java b/src/main/java/com/minelittlepony/unicopia/client/gui/Slot.java index a599a885..6c7dcec7 100644 --- a/src/main/java/com/minelittlepony/unicopia/client/gui/Slot.java +++ b/src/main/java/com/minelittlepony/unicopia/client/gui/Slot.java @@ -5,7 +5,6 @@ import com.minelittlepony.unicopia.ability.AbilitySlot; import com.minelittlepony.unicopia.client.KeyBindingsHandler; import com.mojang.blaze3d.systems.RenderSystem; -import net.minecraft.client.render.GameRenderer; import net.minecraft.client.util.math.MatrixStack; import net.minecraft.text.Text; import net.minecraft.util.math.MathHelper; @@ -102,8 +101,6 @@ class Slot { UHud.fill(matrices, slotPadding, progressTop, size - slotPadding, progressBottom, 0xCFFFFFFF); } - RenderSystem.setShader(GameRenderer::getPositionColorShader); - renderContents(matrices, abilities, bSwap, tickDelta); matrices.pop(); } @@ -114,7 +111,7 @@ class Slot { } void renderLabel(MatrixStack matrices, AbilityDispatcher abilities, float tickDelta) { - Text label = KeyBindingsHandler.INSTANCE.getBinding(aSlot).getBoundKeyLocalizedText(); + Text label = KeyBindingsHandler.INSTANCE.getBinding(aSlot).getLabel(); matrices.push(); diff --git a/src/main/java/com/minelittlepony/unicopia/client/gui/UHud.java b/src/main/java/com/minelittlepony/unicopia/client/gui/UHud.java index 5477244c..b2557e87 100644 --- a/src/main/java/com/minelittlepony/unicopia/client/gui/UHud.java +++ b/src/main/java/com/minelittlepony/unicopia/client/gui/UHud.java @@ -121,8 +121,8 @@ public class UHud extends DrawableHelper { matrices.pop(); if (pony.getSpecies().canCast()) { - renderSpell(pony.getCharms().getEquippedSpell(Hand.MAIN_HAND), hudX + 15 - xDirection * 8, hudY + 2); - renderSpell(pony.getCharms().getEquippedSpell(Hand.OFF_HAND), hudX + 15 - xDirection * 0, hudY - 3); + renderSpell(pony.getCharms().getEquippedSpell(Hand.MAIN_HAND), hudX + 15 - xDirection * 13, hudY + 3); + renderSpell(pony.getCharms().getEquippedSpell(Hand.OFF_HAND), hudX + 15 - xDirection * 2, hudY - 3); } RenderSystem.disableBlend(); @@ -157,11 +157,24 @@ public class UHud extends DrawableHelper { private void renderSpell(CustomisedSpellType spell, int x, int y) { if (!spell.isEmpty()) { MatrixStack modelStack = RenderSystem.getModelViewStack(); + modelStack.push(); modelStack.translate(x, y, 0); - modelStack.scale(EQUIPPED_GEMSTONE_SCALE, EQUIPPED_GEMSTONE_SCALE, EQUIPPED_GEMSTONE_SCALE); + modelStack.scale(EQUIPPED_GEMSTONE_SCALE, EQUIPPED_GEMSTONE_SCALE, 1); + RenderSystem.applyModelViewMatrix(); client.getItemRenderer().renderGuiItemIcon(spell.getDefaultStack(), 0, 0); + + modelStack.loadIdentity(); + modelStack.translate(7.5, 8.5, 0); + + Pony pony = Pony.of(client.player); + + if (spell.isOn(pony)) { + ManaRingSlot.renderArc(modelStack, 5, 7, 0, Math.PI * 2, + (spell.type().getColor() | 0x000000FF) & 0xFFFFFFAF, false); + ManaRingSlot.renderArc(modelStack, 7, 8, 0, Math.PI * 2, 0xAAAAFFFF, false); + } modelStack.pop(); RenderSystem.applyModelViewMatrix(); }