feat: Add basic combo buttons (without editor for now)
This commit is contained in:
@@ -2,18 +2,19 @@
|
|||||||
|
|
||||||
package moe.nea.firmament.mixins;
|
package moe.nea.firmament.mixins;
|
||||||
|
|
||||||
|
import com.llamalad7.mixinextras.injector.v2.WrapWithCondition;
|
||||||
import moe.nea.firmament.events.WorldKeyboardEvent;
|
import moe.nea.firmament.events.WorldKeyboardEvent;
|
||||||
import net.minecraft.client.Keyboard;
|
import net.minecraft.client.Keyboard;
|
||||||
|
import net.minecraft.client.util.InputUtil;
|
||||||
import org.spongepowered.asm.mixin.Mixin;
|
import org.spongepowered.asm.mixin.Mixin;
|
||||||
import org.spongepowered.asm.mixin.injection.At;
|
import org.spongepowered.asm.mixin.injection.At;
|
||||||
import org.spongepowered.asm.mixin.injection.Inject;
|
|
||||||
import org.spongepowered.asm.mixin.injection.callback.CallbackInfo;
|
|
||||||
|
|
||||||
@Mixin(Keyboard.class)
|
@Mixin(Keyboard.class)
|
||||||
public class KeyPressInWorldEventPatch {
|
public class KeyPressInWorldEventPatch {
|
||||||
|
|
||||||
@Inject(method = "onKey", at = @At(value = "INVOKE", target = "Lnet/minecraft/client/option/KeyBinding;onKeyPressed(Lnet/minecraft/client/util/InputUtil$Key;)V"))
|
@WrapWithCondition(method = "onKey", at = @At(value = "INVOKE", target = "Lnet/minecraft/client/option/KeyBinding;onKeyPressed(Lnet/minecraft/client/util/InputUtil$Key;)V"))
|
||||||
public void onKeyBoardInWorld(long window, int key, int scancode, int action, int modifiers, CallbackInfo ci) {
|
public boolean onKeyBoardInWorld(InputUtil.Key key, long window, int _key, int scancode, int action, int modifiers) {
|
||||||
WorldKeyboardEvent.Companion.publish(new WorldKeyboardEvent(key, scancode, modifiers));
|
var event = WorldKeyboardEvent.Companion.publish(new WorldKeyboardEvent(_key, scancode, modifiers));
|
||||||
}
|
return !event.getCancelled();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
104
src/main/kotlin/features/macros/ComboProcessor.kt
Normal file
104
src/main/kotlin/features/macros/ComboProcessor.kt
Normal file
@@ -0,0 +1,104 @@
|
|||||||
|
package moe.nea.firmament.features.macros
|
||||||
|
|
||||||
|
import kotlin.time.Duration.Companion.seconds
|
||||||
|
import net.minecraft.client.util.InputUtil
|
||||||
|
import net.minecraft.text.Text
|
||||||
|
import moe.nea.firmament.annotations.Subscribe
|
||||||
|
import moe.nea.firmament.events.HudRenderEvent
|
||||||
|
import moe.nea.firmament.events.TickEvent
|
||||||
|
import moe.nea.firmament.events.WorldKeyboardEvent
|
||||||
|
import moe.nea.firmament.keybindings.SavedKeyBinding
|
||||||
|
import moe.nea.firmament.util.MC
|
||||||
|
import moe.nea.firmament.util.TimeMark
|
||||||
|
|
||||||
|
object ComboProcessor {
|
||||||
|
|
||||||
|
var rootTrie: Branch = Branch(mapOf())
|
||||||
|
private set
|
||||||
|
|
||||||
|
var activeTrie: Branch = rootTrie
|
||||||
|
private set
|
||||||
|
|
||||||
|
var isInputting = false
|
||||||
|
var lastInput = TimeMark.farPast()
|
||||||
|
val breadCrumbs = mutableListOf<SavedKeyBinding>()
|
||||||
|
// TODO: keep breadcrumbs
|
||||||
|
|
||||||
|
|
||||||
|
init {
|
||||||
|
val f = SavedKeyBinding(InputUtil.GLFW_KEY_F)
|
||||||
|
val one = SavedKeyBinding(InputUtil.GLFW_KEY_1)
|
||||||
|
val two = SavedKeyBinding(InputUtil.GLFW_KEY_2)
|
||||||
|
setActions(
|
||||||
|
listOf(
|
||||||
|
ComboKeyAction(CommandAction("wardrobe"), listOf(f, one)),
|
||||||
|
ComboKeyAction(CommandAction("equipment"), listOf(f, two)),
|
||||||
|
)
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
fun setActions(actions: List<ComboKeyAction>) {
|
||||||
|
rootTrie = KeyComboTrie.fromComboList(actions)
|
||||||
|
reset()
|
||||||
|
}
|
||||||
|
|
||||||
|
fun reset() {
|
||||||
|
activeTrie = rootTrie
|
||||||
|
lastInput = TimeMark.now()
|
||||||
|
isInputting = false
|
||||||
|
breadCrumbs.clear()
|
||||||
|
}
|
||||||
|
|
||||||
|
@Subscribe
|
||||||
|
fun onTick(event: TickEvent) {
|
||||||
|
if (isInputting && lastInput.passedTime() > 3.seconds)
|
||||||
|
reset()
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@Subscribe
|
||||||
|
fun onRender(event: HudRenderEvent) {
|
||||||
|
if (!isInputting) return
|
||||||
|
if (!event.isRenderingHud) return
|
||||||
|
event.context.matrices.push()
|
||||||
|
val width = 120
|
||||||
|
event.context.matrices.translate(
|
||||||
|
(MC.window.scaledWidth - width) / 2F,
|
||||||
|
(MC.window.scaledHeight) / 2F + 8,
|
||||||
|
0F
|
||||||
|
)
|
||||||
|
val breadCrumbText = breadCrumbs.joinToString(" > ")
|
||||||
|
event.context.drawText(MC.font, breadCrumbText, 0, 0, -1, true)
|
||||||
|
event.context.matrices.translate(0F, MC.font.fontHeight + 2F, 0F)
|
||||||
|
for ((key, value) in activeTrie.nodes) {
|
||||||
|
event.context.drawText(MC.font, Text.literal("$breadCrumbText > $key: ").append(value.label), 0, 0, -1, true)
|
||||||
|
event.context.matrices.translate(0F, MC.font.fontHeight + 1F, 0F)
|
||||||
|
}
|
||||||
|
event.context.matrices.pop()
|
||||||
|
}
|
||||||
|
|
||||||
|
@Subscribe
|
||||||
|
fun onKeyBinding(event: WorldKeyboardEvent) {
|
||||||
|
val nextEntry = activeTrie.nodes.entries
|
||||||
|
.find { event.matches(it.key) }
|
||||||
|
if (nextEntry == null) {
|
||||||
|
reset()
|
||||||
|
return
|
||||||
|
}
|
||||||
|
event.cancel()
|
||||||
|
breadCrumbs.add(nextEntry.key)
|
||||||
|
lastInput = TimeMark.now()
|
||||||
|
isInputting = true
|
||||||
|
val value = nextEntry.value
|
||||||
|
when (value) {
|
||||||
|
is Branch -> {
|
||||||
|
activeTrie = value
|
||||||
|
}
|
||||||
|
|
||||||
|
is Leaf -> {
|
||||||
|
value.execute()
|
||||||
|
reset()
|
||||||
|
}
|
||||||
|
}.let { }
|
||||||
|
}
|
||||||
|
}
|
||||||
35
src/main/kotlin/features/macros/HotkeyAction.kt
Normal file
35
src/main/kotlin/features/macros/HotkeyAction.kt
Normal file
@@ -0,0 +1,35 @@
|
|||||||
|
package moe.nea.firmament.features.macros
|
||||||
|
|
||||||
|
import net.minecraft.text.Text
|
||||||
|
import moe.nea.firmament.util.MC
|
||||||
|
|
||||||
|
interface HotkeyAction {
|
||||||
|
// TODO: execute
|
||||||
|
val label: Text
|
||||||
|
fun execute()
|
||||||
|
}
|
||||||
|
|
||||||
|
data class CommandAction(val command: String) : HotkeyAction {
|
||||||
|
override val label: Text
|
||||||
|
get() = Text.literal("/$command")
|
||||||
|
|
||||||
|
override fun execute() {
|
||||||
|
MC.sendCommand(command)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Mit onscreen anzeige:
|
||||||
|
// F -> 1 /equipment
|
||||||
|
// F -> 2 /wardrobe
|
||||||
|
// Bei Combos: Keys buffern! (für wardrobe hotkeys beispielsweiße)
|
||||||
|
|
||||||
|
// Radial menu
|
||||||
|
// Hold F
|
||||||
|
// Weight (mach eins doppelt so groß)
|
||||||
|
// /equipment
|
||||||
|
// /wardrobe
|
||||||
|
|
||||||
|
// Bei allen: Filter!
|
||||||
|
// - Nur in Dungeons / andere Insel
|
||||||
|
// - Nur wenn ich Item X im inventar habe (fishing rod)
|
||||||
|
|
||||||
57
src/main/kotlin/features/macros/KeyComboTrie.kt
Normal file
57
src/main/kotlin/features/macros/KeyComboTrie.kt
Normal file
@@ -0,0 +1,57 @@
|
|||||||
|
package moe.nea.firmament.features.macros
|
||||||
|
|
||||||
|
import net.minecraft.text.Text
|
||||||
|
import moe.nea.firmament.keybindings.SavedKeyBinding
|
||||||
|
|
||||||
|
sealed interface KeyComboTrie {
|
||||||
|
val label: Text
|
||||||
|
|
||||||
|
companion object {
|
||||||
|
fun fromComboList(
|
||||||
|
combos: List<ComboKeyAction>,
|
||||||
|
): Branch {
|
||||||
|
val root = Branch(mutableMapOf())
|
||||||
|
for (combo in combos) {
|
||||||
|
var p = root
|
||||||
|
require(combo.keys.isNotEmpty())
|
||||||
|
for ((index, key) in combo.keys.withIndex()) {
|
||||||
|
val m = (p.nodes as MutableMap)
|
||||||
|
if (index == combo.keys.lastIndex) {
|
||||||
|
if (key in m)
|
||||||
|
error("Overlapping actions found for ${combo.keys} (another action ${m[key]} already exists).")
|
||||||
|
|
||||||
|
m[key] = Leaf(combo.action)
|
||||||
|
} else {
|
||||||
|
val c = m.getOrPut(key) { Branch(mutableMapOf()) }
|
||||||
|
if (c !is Branch)
|
||||||
|
error("Overlapping actions found for ${combo.keys} (final node exists at index $index) through another action already")
|
||||||
|
p = c
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return root
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
data class ComboKeyAction(
|
||||||
|
val action: HotkeyAction,
|
||||||
|
val keys: List<SavedKeyBinding>,
|
||||||
|
)
|
||||||
|
|
||||||
|
data class Leaf(val action: HotkeyAction) : KeyComboTrie {
|
||||||
|
override val label: Text
|
||||||
|
get() = action.label
|
||||||
|
|
||||||
|
fun execute() {
|
||||||
|
action.execute()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
data class Branch(
|
||||||
|
val nodes: Map<SavedKeyBinding, KeyComboTrie>
|
||||||
|
) : KeyComboTrie {
|
||||||
|
override val label: Text
|
||||||
|
get() = Text.literal("...") // TODO: better labels
|
||||||
|
}
|
||||||
@@ -87,6 +87,10 @@ data class SavedKeyBinding(
|
|||||||
return keyCode == this.keyCode && getMods(modifiers) == Triple(shift, ctrl, alt)
|
return keyCode == this.keyCode && getMods(modifiers) == Triple(shift, ctrl, alt)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
override fun toString(): String {
|
||||||
|
return format().string
|
||||||
|
}
|
||||||
|
|
||||||
fun format(): Text {
|
fun format(): Text {
|
||||||
val stroke = Text.literal("")
|
val stroke = Text.literal("")
|
||||||
if (ctrl) {
|
if (ctrl) {
|
||||||
|
|||||||
103
src/test/kotlin/features/macros/KeyComboTrieCreation.kt
Normal file
103
src/test/kotlin/features/macros/KeyComboTrieCreation.kt
Normal file
@@ -0,0 +1,103 @@
|
|||||||
|
package moe.nea.firmament.test.features.macros
|
||||||
|
|
||||||
|
import org.junit.jupiter.api.Assertions
|
||||||
|
import org.junit.jupiter.api.Test
|
||||||
|
import net.minecraft.client.util.InputUtil
|
||||||
|
import moe.nea.firmament.features.macros.Branch
|
||||||
|
import moe.nea.firmament.features.macros.ComboKeyAction
|
||||||
|
import moe.nea.firmament.features.macros.CommandAction
|
||||||
|
import moe.nea.firmament.features.macros.KeyComboTrie
|
||||||
|
import moe.nea.firmament.features.macros.Leaf
|
||||||
|
import moe.nea.firmament.keybindings.SavedKeyBinding
|
||||||
|
|
||||||
|
class KeyComboTrieCreation {
|
||||||
|
val basicAction = CommandAction("ac Hello")
|
||||||
|
val aPress = SavedKeyBinding(InputUtil.GLFW_KEY_A)
|
||||||
|
val bPress = SavedKeyBinding(InputUtil.GLFW_KEY_B)
|
||||||
|
val cPress = SavedKeyBinding(InputUtil.GLFW_KEY_C)
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun testValidShortTrie() {
|
||||||
|
val actions = listOf(
|
||||||
|
ComboKeyAction(basicAction, listOf(aPress)),
|
||||||
|
ComboKeyAction(basicAction, listOf(bPress)),
|
||||||
|
ComboKeyAction(basicAction, listOf(cPress)),
|
||||||
|
)
|
||||||
|
Assertions.assertEquals(
|
||||||
|
Branch(
|
||||||
|
mapOf(
|
||||||
|
aPress to Leaf(basicAction),
|
||||||
|
bPress to Leaf(basicAction),
|
||||||
|
cPress to Leaf(basicAction),
|
||||||
|
),
|
||||||
|
), KeyComboTrie.fromComboList(actions)
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun testOverlappingLeafs() {
|
||||||
|
Assertions.assertThrows(IllegalStateException::class.java) {
|
||||||
|
KeyComboTrie.fromComboList(
|
||||||
|
listOf(
|
||||||
|
ComboKeyAction(basicAction, listOf(aPress, aPress)),
|
||||||
|
ComboKeyAction(basicAction, listOf(aPress, aPress)),
|
||||||
|
)
|
||||||
|
)
|
||||||
|
}
|
||||||
|
Assertions.assertThrows(IllegalStateException::class.java) {
|
||||||
|
KeyComboTrie.fromComboList(
|
||||||
|
listOf(
|
||||||
|
ComboKeyAction(basicAction, listOf(aPress)),
|
||||||
|
ComboKeyAction(basicAction, listOf(aPress)),
|
||||||
|
)
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun testBranchOverlappingLeaf() {
|
||||||
|
Assertions.assertThrows(IllegalStateException::class.java) {
|
||||||
|
KeyComboTrie.fromComboList(
|
||||||
|
listOf(
|
||||||
|
ComboKeyAction(basicAction, listOf(aPress)),
|
||||||
|
ComboKeyAction(basicAction, listOf(aPress, aPress)),
|
||||||
|
)
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
@Test
|
||||||
|
fun testLeafOverlappingBranch() {
|
||||||
|
Assertions.assertThrows(IllegalStateException::class.java) {
|
||||||
|
KeyComboTrie.fromComboList(
|
||||||
|
listOf(
|
||||||
|
ComboKeyAction(basicAction, listOf(aPress, aPress)),
|
||||||
|
ComboKeyAction(basicAction, listOf(aPress)),
|
||||||
|
)
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun testValidNestedTrie() {
|
||||||
|
val actions = listOf(
|
||||||
|
ComboKeyAction(basicAction, listOf(aPress, aPress)),
|
||||||
|
ComboKeyAction(basicAction, listOf(aPress, bPress)),
|
||||||
|
ComboKeyAction(basicAction, listOf(cPress)),
|
||||||
|
)
|
||||||
|
Assertions.assertEquals(
|
||||||
|
Branch(
|
||||||
|
mapOf(
|
||||||
|
aPress to Branch(
|
||||||
|
mapOf(
|
||||||
|
aPress to Leaf(basicAction),
|
||||||
|
bPress to Leaf(basicAction),
|
||||||
|
)
|
||||||
|
),
|
||||||
|
cPress to Leaf(basicAction),
|
||||||
|
),
|
||||||
|
), KeyComboTrie.fromComboList(actions)
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user