can now save and cast spells from spellbooks!
This commit is contained in:
parent
57b3b2854c
commit
fd5f1087fd
|
@ -1,6 +1,7 @@
|
||||||
package at.petrak.hex
|
package at.petrak.hex
|
||||||
|
|
||||||
import net.minecraft.nbt.LongArrayTag
|
import net.minecraft.nbt.LongArrayTag
|
||||||
|
import net.minecraft.world.InteractionHand
|
||||||
import net.minecraft.world.phys.Vec2
|
import net.minecraft.world.phys.Vec2
|
||||||
import net.minecraft.world.phys.Vec3
|
import net.minecraft.world.phys.Vec3
|
||||||
|
|
||||||
|
@ -10,7 +11,7 @@ object HexUtils {
|
||||||
LongArrayTag(longArrayOf(this.x.toRawBits(), this.y.toRawBits(), this.z.toRawBits()))
|
LongArrayTag(longArrayOf(this.x.toRawBits(), this.y.toRawBits(), this.z.toRawBits()))
|
||||||
|
|
||||||
@JvmStatic
|
@JvmStatic
|
||||||
fun deserializeVec3FromNBT(tag: LongArray): Vec3 =
|
fun DeserializeVec3FromNBT(tag: LongArray): Vec3 =
|
||||||
Vec3(
|
Vec3(
|
||||||
Double.fromBits(tag[0]),
|
Double.fromBits(tag[0]),
|
||||||
Double.fromBits(tag[1]),
|
Double.fromBits(tag[1]),
|
||||||
|
@ -22,11 +23,15 @@ object HexUtils {
|
||||||
LongArrayTag(longArrayOf(this.x.toDouble().toRawBits(), this.y.toDouble().toRawBits()))
|
LongArrayTag(longArrayOf(this.x.toDouble().toRawBits(), this.y.toDouble().toRawBits()))
|
||||||
|
|
||||||
@JvmStatic
|
@JvmStatic
|
||||||
fun deserializeVec2FromNBT(tag: LongArray): Vec2 =
|
fun DeserializeVec2FromNBT(tag: LongArray): Vec2 =
|
||||||
Vec2(
|
Vec2(
|
||||||
Double.fromBits(tag[0]).toFloat(),
|
Double.fromBits(tag[0]).toFloat(),
|
||||||
Double.fromBits(tag[1]).toFloat(),
|
Double.fromBits(tag[1]).toFloat(),
|
||||||
)
|
)
|
||||||
|
|
||||||
|
@JvmStatic
|
||||||
|
fun OtherHand(hand: InteractionHand) =
|
||||||
|
if (hand == InteractionHand.MAIN_HAND) InteractionHand.OFF_HAND else InteractionHand.MAIN_HAND
|
||||||
|
|
||||||
const val TAU = Math.PI * 2.0
|
const val TAU = Math.PI * 2.0
|
||||||
}
|
}
|
|
@ -17,7 +17,6 @@ import net.minecraftforge.fml.common.Mod;
|
||||||
public class ShiftScrollListener {
|
public class ShiftScrollListener {
|
||||||
@SubscribeEvent
|
@SubscribeEvent
|
||||||
public static void onScroll(InputEvent.MouseScrollEvent evt) {
|
public static void onScroll(InputEvent.MouseScrollEvent evt) {
|
||||||
HexMod.LOGGER.info("scrolling {}", evt.getScrollDelta());
|
|
||||||
LocalPlayer player = Minecraft.getInstance().player;
|
LocalPlayer player = Minecraft.getInstance().player;
|
||||||
if (player.isCrouching()) {
|
if (player.isCrouching()) {
|
||||||
InteractionHand hand = null;
|
InteractionHand hand = null;
|
||||||
|
|
|
@ -1,7 +1,11 @@
|
||||||
package at.petrak.hex.client.gui
|
package at.petrak.hex.client.gui
|
||||||
|
|
||||||
import at.petrak.hex.HexMod
|
import at.petrak.hex.HexMod
|
||||||
|
import at.petrak.hex.HexUtils
|
||||||
import at.petrak.hex.HexUtils.TAU
|
import at.petrak.hex.HexUtils.TAU
|
||||||
|
import at.petrak.hex.common.items.ItemSpellbook
|
||||||
|
import at.petrak.hex.common.network.HexMessages
|
||||||
|
import at.petrak.hex.common.network.MsgShiftScrollSyn
|
||||||
import at.petrak.hex.hexmath.HexAngle
|
import at.petrak.hex.hexmath.HexAngle
|
||||||
import at.petrak.hex.hexmath.HexCoord
|
import at.petrak.hex.hexmath.HexCoord
|
||||||
import at.petrak.hex.hexmath.HexDir
|
import at.petrak.hex.hexmath.HexDir
|
||||||
|
@ -101,7 +105,7 @@ class GuiSpellcasting(private val handOpenedWith: InteractionHand) : Screen(Text
|
||||||
this.drawState = PatternDrawState.BetweenPatterns
|
this.drawState = PatternDrawState.BetweenPatterns
|
||||||
this.patterns.add(Pair(pat, start))
|
this.patterns.add(Pair(pat, start))
|
||||||
|
|
||||||
at.petrak.hex.common.network.HexMessages.getNetwork().sendToServer(
|
HexMessages.getNetwork().sendToServer(
|
||||||
at.petrak.hex.common.network.MsgNewSpellPatternSyn(
|
at.petrak.hex.common.network.MsgNewSpellPatternSyn(
|
||||||
this.handOpenedWith,
|
this.handOpenedWith,
|
||||||
pat
|
pat
|
||||||
|
@ -113,6 +117,16 @@ class GuiSpellcasting(private val handOpenedWith: InteractionHand) : Screen(Text
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
|
|
||||||
|
override fun mouseScrolled(pMouseX: Double, pMouseY: Double, pDelta: Double): Boolean {
|
||||||
|
super.mouseScrolled(pMouseX, pMouseY, pDelta)
|
||||||
|
|
||||||
|
val otherHand = HexUtils.OtherHand(this.handOpenedWith)
|
||||||
|
if (Minecraft.getInstance().player!!.getItemInHand(otherHand).item is ItemSpellbook)
|
||||||
|
HexMessages.getNetwork().sendToServer(MsgShiftScrollSyn(otherHand, pDelta))
|
||||||
|
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
||||||
override fun onClose() {
|
override fun onClose() {
|
||||||
at.petrak.hex.common.network.HexMessages.getNetwork()
|
at.petrak.hex.common.network.HexMessages.getNetwork()
|
||||||
.sendToServer(at.petrak.hex.common.network.MsgQuitSpellcasting())
|
.sendToServer(at.petrak.hex.common.network.MsgQuitSpellcasting())
|
||||||
|
|
|
@ -50,6 +50,13 @@ class CastException(val reason: Reason, vararg val data: Any) : Exception() {
|
||||||
* `vec: Vec3`
|
* `vec: Vec3`
|
||||||
*/
|
*/
|
||||||
TOO_FAR,
|
TOO_FAR,
|
||||||
|
|
||||||
|
/**
|
||||||
|
* An operator needed a spellbook in the offhand.
|
||||||
|
*
|
||||||
|
* `no args`
|
||||||
|
*/
|
||||||
|
REQUIRES_SPELLBOOK,
|
||||||
}
|
}
|
||||||
|
|
||||||
override val message: String
|
override val message: String
|
||||||
|
@ -60,5 +67,6 @@ class CastException(val reason: Reason, vararg val data: Any) : Exception() {
|
||||||
Reason.NOT_ENOUGH_ARGS -> "required at least ${this.data[0] as Int} args on the stack but only had ${this.data[1] as Int}"
|
Reason.NOT_ENOUGH_ARGS -> "required at least ${this.data[0] as Int} args on the stack but only had ${this.data[1] as Int}"
|
||||||
Reason.TOO_MANY_CLOSE_PARENS -> "too many close parentheses"
|
Reason.TOO_MANY_CLOSE_PARENS -> "too many close parentheses"
|
||||||
Reason.TOO_FAR -> "tried to interact with something too far away at ${this.data[0] as Vec3}"
|
Reason.TOO_FAR -> "tried to interact with something too far away at ${this.data[0] as Vec3}"
|
||||||
|
Reason.REQUIRES_SPELLBOOK -> "required a spellbook in the other hand"
|
||||||
}
|
}
|
||||||
}
|
}
|
|
@ -1,7 +1,11 @@
|
||||||
package at.petrak.hex.common.casting
|
package at.petrak.hex.common.casting
|
||||||
|
|
||||||
|
import at.petrak.hex.HexUtils
|
||||||
|
import at.petrak.hex.common.items.ItemSpellbook
|
||||||
import net.minecraft.server.level.ServerLevel
|
import net.minecraft.server.level.ServerLevel
|
||||||
import net.minecraft.server.level.ServerPlayer
|
import net.minecraft.server.level.ServerPlayer
|
||||||
|
import net.minecraft.world.InteractionHand
|
||||||
|
import net.minecraft.world.item.ItemStack
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Info about the moment the spell started being cast.
|
* Info about the moment the spell started being cast.
|
||||||
|
@ -9,6 +13,17 @@ import net.minecraft.server.level.ServerPlayer
|
||||||
@JvmRecord
|
@JvmRecord
|
||||||
data class CastingContext(
|
data class CastingContext(
|
||||||
val caster: ServerPlayer,
|
val caster: ServerPlayer,
|
||||||
|
val wandHand: InteractionHand,
|
||||||
) {
|
) {
|
||||||
val world: ServerLevel get() = caster.getLevel()
|
val world: ServerLevel get() = caster.getLevel()
|
||||||
|
|
||||||
|
fun getSpellbook(): ItemStack {
|
||||||
|
val handItem =
|
||||||
|
caster.getItemInHand(HexUtils.OtherHand(wandHand))
|
||||||
|
return if (handItem.item is ItemSpellbook) {
|
||||||
|
handItem
|
||||||
|
} else {
|
||||||
|
throw CastException(CastException.Reason.REQUIRES_SPELLBOOK)
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -6,6 +6,7 @@ import net.minecraft.nbt.CompoundTag
|
||||||
import net.minecraft.nbt.ListTag
|
import net.minecraft.nbt.ListTag
|
||||||
import net.minecraft.nbt.Tag
|
import net.minecraft.nbt.Tag
|
||||||
import net.minecraft.server.level.ServerPlayer
|
import net.minecraft.server.level.ServerPlayer
|
||||||
|
import net.minecraft.world.InteractionHand
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Keeps track of a player casting a spell on the server.
|
* Keeps track of a player casting a spell on the server.
|
||||||
|
@ -143,8 +144,8 @@ class CastingHarness private constructor(
|
||||||
const val TAG_ESCAPE_NEXT = "escape_next"
|
const val TAG_ESCAPE_NEXT = "escape_next"
|
||||||
|
|
||||||
@JvmStatic
|
@JvmStatic
|
||||||
fun DeserializeFromNBT(nbt: Tag?, caster: ServerPlayer): CastingHarness {
|
fun DeserializeFromNBT(nbt: Tag?, caster: ServerPlayer, wandHand: InteractionHand): CastingHarness {
|
||||||
val ctx = CastingContext(caster)
|
val ctx = CastingContext(caster, wandHand)
|
||||||
return try {
|
return try {
|
||||||
val nbt = nbt as CompoundTag
|
val nbt = nbt as CompoundTag
|
||||||
|
|
||||||
|
|
|
@ -114,7 +114,7 @@ class SpellDatum<T : Any> private constructor(val payload: T) {
|
||||||
SpellDatum(if (entity == null || !entity.isAlive) SpellWidget.NULL else entity)
|
SpellDatum(if (entity == null || !entity.isAlive) SpellWidget.NULL else entity)
|
||||||
}
|
}
|
||||||
TAG_DOUBLE -> SpellDatum(nbt.getDouble(key))
|
TAG_DOUBLE -> SpellDatum(nbt.getDouble(key))
|
||||||
TAG_VEC3 -> SpellDatum(HexUtils.deserializeVec3FromNBT(nbt.getLongArray(key)))
|
TAG_VEC3 -> SpellDatum(HexUtils.DeserializeVec3FromNBT(nbt.getLongArray(key)))
|
||||||
TAG_LIST -> {
|
TAG_LIST -> {
|
||||||
val arr = nbt.getList(key, Tag.TAG_COMPOUND.toInt())
|
val arr = nbt.getList(key, Tag.TAG_COMPOUND.toInt())
|
||||||
val out = ArrayList<SpellDatum<*>>(arr.size)
|
val out = ArrayList<SpellDatum<*>>(arr.size)
|
||||||
|
|
|
@ -65,10 +65,12 @@ interface SpellOperator {
|
||||||
// == Meta stuff ==
|
// == Meta stuff ==
|
||||||
"qqq" to SpellWidget.OPEN_PAREN,
|
"qqq" to SpellWidget.OPEN_PAREN,
|
||||||
"eee" to SpellWidget.CLOSE_PAREN,
|
"eee" to SpellWidget.CLOSE_PAREN,
|
||||||
"aaaqw" to SpellWidget.ESCAPE,
|
"qqqaw" to SpellWidget.ESCAPE,
|
||||||
// http://www.toroidalsnark.net/mkss3-pix/CalderheadJMM2014.pdf
|
// http://www.toroidalsnark.net/mkss3-pix/CalderheadJMM2014.pdf
|
||||||
// eval being a space filling curve feels apt doesn't it
|
// eval being a space filling curve feels apt doesn't it
|
||||||
"deaqq" to OpEval
|
"deaqq" to OpEval,
|
||||||
|
"aqqqqq" to OpReadFromSpellbook,
|
||||||
|
"deeeee" to OpWriteToSpellbook,
|
||||||
)
|
)
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
|
@ -0,0 +1,17 @@
|
||||||
|
package at.petrak.hex.common.casting.operators
|
||||||
|
|
||||||
|
import at.petrak.hex.common.casting.CastingContext
|
||||||
|
import at.petrak.hex.common.casting.SpellDatum
|
||||||
|
import at.petrak.hex.common.casting.SpellWidget
|
||||||
|
import at.petrak.hex.common.items.ItemSpellbook
|
||||||
|
|
||||||
|
object OpReadFromSpellbook : SimpleOperator {
|
||||||
|
override val argc: Int
|
||||||
|
get() = 0
|
||||||
|
|
||||||
|
override fun execute(args: List<SpellDatum<*>>, ctx: CastingContext): List<SpellDatum<*>> {
|
||||||
|
val spellbook = ctx.getSpellbook()
|
||||||
|
val datum = ItemSpellbook.ReadDatum(spellbook.orCreateTag, ctx)
|
||||||
|
return listOf(datum ?: SpellDatum.make(SpellWidget.NULL))
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,17 @@
|
||||||
|
package at.petrak.hex.common.casting.operators
|
||||||
|
|
||||||
|
import at.petrak.hex.common.casting.CastingContext
|
||||||
|
import at.petrak.hex.common.casting.SpellDatum
|
||||||
|
import at.petrak.hex.common.casting.SpellOperator.Companion.spellListOf
|
||||||
|
import at.petrak.hex.common.items.ItemSpellbook
|
||||||
|
|
||||||
|
object OpWriteToSpellbook : SimpleOperator {
|
||||||
|
override val argc: Int
|
||||||
|
get() = 1
|
||||||
|
|
||||||
|
override fun execute(args: List<SpellDatum<*>>, ctx: CastingContext): List<SpellDatum<*>> {
|
||||||
|
val spellbook = ctx.getSpellbook()
|
||||||
|
ItemSpellbook.WriteDatum(spellbook.orCreateTag, args[0])
|
||||||
|
return spellListOf()
|
||||||
|
}
|
||||||
|
}
|
|
@ -1,9 +1,12 @@
|
||||||
package at.petrak.hex.common.items;
|
package at.petrak.hex.common.items;
|
||||||
|
|
||||||
|
import at.petrak.hex.common.casting.CastingContext;
|
||||||
|
import at.petrak.hex.common.casting.SpellDatum;
|
||||||
import net.minecraft.nbt.CompoundTag;
|
import net.minecraft.nbt.CompoundTag;
|
||||||
import net.minecraft.network.chat.Component;
|
import net.minecraft.network.chat.Component;
|
||||||
import net.minecraft.network.chat.TextComponent;
|
import net.minecraft.network.chat.TextComponent;
|
||||||
import net.minecraft.network.chat.TranslatableComponent;
|
import net.minecraft.network.chat.TranslatableComponent;
|
||||||
|
import net.minecraft.world.entity.Entity;
|
||||||
import net.minecraft.world.item.Item;
|
import net.minecraft.world.item.Item;
|
||||||
import net.minecraft.world.item.ItemStack;
|
import net.minecraft.world.item.ItemStack;
|
||||||
import net.minecraft.world.item.TooltipFlag;
|
import net.minecraft.world.item.TooltipFlag;
|
||||||
|
@ -15,7 +18,8 @@ import java.util.stream.Stream;
|
||||||
|
|
||||||
public class ItemSpellbook extends Item {
|
public class ItemSpellbook extends Item {
|
||||||
public static String TAG_SELECTED_PAGE = "page_idx";
|
public static String TAG_SELECTED_PAGE = "page_idx";
|
||||||
// this is a CompoundTag of string numerical keys to SpellData
|
// this is a CompoundTag of string numerical keys to SpellData\
|
||||||
|
// it is 1-indexed, so that 0/0 can be the special case of "it is empty"
|
||||||
public static String TAG_PAGES = "pages";
|
public static String TAG_PAGES = "pages";
|
||||||
|
|
||||||
public ItemSpellbook(Properties properties) {
|
public ItemSpellbook(Properties properties) {
|
||||||
|
@ -40,6 +44,68 @@ public class ItemSpellbook extends Item {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void inventoryTick(ItemStack stack, Level pLevel, Entity pEntity, int pSlotId, boolean pIsSelected) {
|
||||||
|
var tag = stack.getOrCreateTag();
|
||||||
|
if (ArePagesEmpty(tag)) {
|
||||||
|
tag.putInt(TAG_SELECTED_PAGE, 0);
|
||||||
|
} else if (!tag.contains(TAG_SELECTED_PAGE)) {
|
||||||
|
tag.putInt(TAG_SELECTED_PAGE, 1);
|
||||||
|
} else {
|
||||||
|
var pageIdx = tag.getInt(TAG_SELECTED_PAGE);
|
||||||
|
if (pageIdx == 0) {
|
||||||
|
tag.putInt(TAG_SELECTED_PAGE, 1);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public static boolean ArePagesEmpty(CompoundTag tag) {
|
||||||
|
return !tag.contains(ItemSpellbook.TAG_PAGES) ||
|
||||||
|
tag.getCompound(ItemSpellbook.TAG_PAGES).isEmpty();
|
||||||
|
}
|
||||||
|
|
||||||
|
public static void WriteDatum(CompoundTag tag, SpellDatum<?> datum) {
|
||||||
|
int idx;
|
||||||
|
if (tag.contains(TAG_SELECTED_PAGE)) {
|
||||||
|
idx = tag.getInt(TAG_SELECTED_PAGE);
|
||||||
|
// But we want to write to page *1* to start if this is our first page
|
||||||
|
if (idx == 0 && ArePagesEmpty(tag)) {
|
||||||
|
idx = 1;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
idx = 1;
|
||||||
|
}
|
||||||
|
var key = String.valueOf(idx);
|
||||||
|
if (tag.contains(TAG_PAGES)) {
|
||||||
|
tag.getCompound(TAG_PAGES).put(key, datum.serializeToNBT());
|
||||||
|
} else {
|
||||||
|
var pagesTag = new CompoundTag();
|
||||||
|
pagesTag.put(key, datum.serializeToNBT());
|
||||||
|
tag.put(TAG_PAGES, pagesTag);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Nullable
|
||||||
|
public static SpellDatum<?> ReadDatum(CompoundTag tag, CastingContext ctx) {
|
||||||
|
int idx;
|
||||||
|
if (tag.contains(TAG_SELECTED_PAGE)) {
|
||||||
|
idx = tag.getInt(TAG_SELECTED_PAGE);
|
||||||
|
} else {
|
||||||
|
idx = 0;
|
||||||
|
}
|
||||||
|
var key = String.valueOf(idx);
|
||||||
|
if (tag.contains(TAG_PAGES)) {
|
||||||
|
var pagesTag = tag.getCompound(TAG_PAGES);
|
||||||
|
if (pagesTag.contains(key)) {
|
||||||
|
return SpellDatum.DeserializeFromNBT(pagesTag.getCompound(key), ctx);
|
||||||
|
} else {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
public static int HighestPage(CompoundTag tag) {
|
public static int HighestPage(CompoundTag tag) {
|
||||||
var highestKey = tag.getAllKeys().stream().flatMap(s -> {
|
var highestKey = tag.getAllKeys().stream().flatMap(s -> {
|
||||||
try {
|
try {
|
||||||
|
@ -53,12 +119,14 @@ public class ItemSpellbook extends Item {
|
||||||
|
|
||||||
public static void RotatePageIdx(CompoundTag tag, boolean increase) {
|
public static void RotatePageIdx(CompoundTag tag, boolean increase) {
|
||||||
int newIdx;
|
int newIdx;
|
||||||
if (tag.contains(ItemSpellbook.TAG_SELECTED_PAGE)) {
|
if (ArePagesEmpty(tag)) {
|
||||||
var delta = increase ? 1 : -1;
|
|
||||||
newIdx = Math.max(0, tag.getInt(ItemSpellbook.TAG_SELECTED_PAGE) + delta);
|
|
||||||
} else {
|
|
||||||
newIdx = 0;
|
newIdx = 0;
|
||||||
|
} else if (tag.contains(TAG_SELECTED_PAGE)) {
|
||||||
|
var delta = increase ? 1 : -1;
|
||||||
|
newIdx = Math.max(1, tag.getInt(TAG_SELECTED_PAGE) + delta);
|
||||||
|
} else {
|
||||||
|
newIdx = 1;
|
||||||
}
|
}
|
||||||
tag.putInt(ItemSpellbook.TAG_SELECTED_PAGE, newIdx);
|
tag.putInt(TAG_SELECTED_PAGE, newIdx);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -42,11 +42,11 @@ public record MsgNewSpellPatternSyn(InteractionHand handUsed, HexPattern pattern
|
||||||
var held = sender.getItemInHand(this.handUsed);
|
var held = sender.getItemInHand(this.handUsed);
|
||||||
if (held.getItem() instanceof ItemWand) {
|
if (held.getItem() instanceof ItemWand) {
|
||||||
var tag = held.getOrCreateTag();
|
var tag = held.getOrCreateTag();
|
||||||
var harness = CastingHarness.DeserializeFromNBT(tag, sender);
|
var harness = CastingHarness.DeserializeFromNBT(tag, sender, this.handUsed);
|
||||||
|
|
||||||
var res = harness.update(this.pattern);
|
var res = harness.update(this.pattern);
|
||||||
if (res instanceof CastResult.Success success) {
|
if (res instanceof CastResult.Success success) {
|
||||||
success.getSpell().cast(new CastingContext(sender));
|
success.getSpell().cast(new CastingContext(sender, this.handUsed));
|
||||||
} else if (res instanceof CastResult.Error error) {
|
} else if (res instanceof CastResult.Error error) {
|
||||||
sender.sendMessage(new TextComponent(error.getExn().getMessage()), Util.NIL_UUID);
|
sender.sendMessage(new TextComponent(error.getExn().getMessage()), Util.NIL_UUID);
|
||||||
}
|
}
|
||||||
|
|
|
@ -12,7 +12,8 @@ import net.minecraftforge.network.NetworkEvent;
|
||||||
import java.util.function.Supplier;
|
import java.util.function.Supplier;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Sent client->server when the client shift+scrolls with a shift-scrollable item.
|
* Sent client->server when the client shift+scrolls with a shift-scrollable item
|
||||||
|
* or scrolls in the spellcasting UI.
|
||||||
*/
|
*/
|
||||||
public record MsgShiftScrollSyn(InteractionHand hand, double scrollDelta) {
|
public record MsgShiftScrollSyn(InteractionHand hand, double scrollDelta) {
|
||||||
public static MsgShiftScrollSyn deserialize(ByteBuf buffer) {
|
public static MsgShiftScrollSyn deserialize(ByteBuf buffer) {
|
||||||
|
|
Loading…
Reference in a new issue