Add slot binding
This commit is contained in:
@@ -2,8 +2,8 @@
|
|||||||
package moe.nea.firmament.init;
|
package moe.nea.firmament.init;
|
||||||
|
|
||||||
import me.shedaniel.mm.api.ClassTinkerers;
|
import me.shedaniel.mm.api.ClassTinkerers;
|
||||||
import net.minecraft.client.gui.screen.Screen;
|
import net.minecraft.client.gui.Element;
|
||||||
import net.minecraft.client.gui.screen.ingame.HandledScreen;
|
import net.minecraft.client.gui.ParentElement;
|
||||||
import org.objectweb.asm.Opcodes;
|
import org.objectweb.asm.Opcodes;
|
||||||
import org.objectweb.asm.Type;
|
import org.objectweb.asm.Type;
|
||||||
import org.objectweb.asm.tree.ClassNode;
|
import org.objectweb.asm.tree.ClassNode;
|
||||||
@@ -17,69 +17,139 @@ import org.objectweb.asm.tree.MethodNode;
|
|||||||
import org.objectweb.asm.tree.VarInsnNode;
|
import org.objectweb.asm.tree.VarInsnNode;
|
||||||
|
|
||||||
import java.lang.reflect.Modifier;
|
import java.lang.reflect.Modifier;
|
||||||
|
import java.util.function.Consumer;
|
||||||
|
|
||||||
public class HandledScreenRiser extends RiserUtils {
|
public class HandledScreenRiser extends RiserUtils {
|
||||||
@IntermediaryName(net.minecraft.client.gui.screen.Screen.class)
|
@IntermediaryName(net.minecraft.client.gui.screen.Screen.class)
|
||||||
String Screen;
|
String Screen;
|
||||||
@IntermediaryName(net.minecraft.client.gui.screen.ingame.HandledScreen.class)
|
@IntermediaryName(net.minecraft.client.gui.screen.ingame.HandledScreen.class)
|
||||||
String HandledScreen;
|
String HandledScreen;
|
||||||
Type mouseScrolledDesc = Type.getMethodType(Type.BOOLEAN_TYPE, Type.DOUBLE_TYPE, Type.DOUBLE_TYPE, Type.DOUBLE_TYPE, Type.DOUBLE_TYPE);
|
Type mouseScrolledDesc = Type.getMethodType(Type.BOOLEAN_TYPE, Type.DOUBLE_TYPE, Type.DOUBLE_TYPE, Type.DOUBLE_TYPE, Type.DOUBLE_TYPE);
|
||||||
String mouseScrolled = remapper.mapMethodName("intermediary", "net.minecraft.class_364", "method_25401",
|
String mouseScrolled = remapper.mapMethodName("intermediary", "net.minecraft.class_364", "method_25401",
|
||||||
mouseScrolledDesc.getDescriptor());
|
mouseScrolledDesc.getDescriptor());
|
||||||
|
// boolean keyReleased(int keyCode, int scanCode, int modifiers)
|
||||||
|
Type keyReleasedDesc = Type.getMethodType(Type.BOOLEAN_TYPE, Type.INT_TYPE, Type.INT_TYPE, Type.INT_TYPE);
|
||||||
|
String keyReleased = remapper.mapMethodName("intermediary", Intermediary.<Element>className(),
|
||||||
|
Intermediary.methodName(Element::keyReleased),
|
||||||
|
keyReleasedDesc.getDescriptor());
|
||||||
|
|
||||||
@Override
|
|
||||||
public void addTinkerers() {
|
|
||||||
ClassTinkerers.addTransformation(HandledScreen, this::handle, true);
|
|
||||||
}
|
|
||||||
|
|
||||||
void handle(ClassNode classNode) {
|
@Override
|
||||||
MethodNode mouseScrolledNode = findMethod(classNode, mouseScrolled, mouseScrolledDesc);
|
public void addTinkerers() {
|
||||||
if (mouseScrolledNode == null) {
|
ClassTinkerers.addTransformation(HandledScreen, this::addMouseScroll, true);
|
||||||
mouseScrolledNode = new MethodNode(
|
ClassTinkerers.addTransformation(HandledScreen, this::addKeyReleased, true);
|
||||||
Modifier.PUBLIC,
|
}
|
||||||
mouseScrolled,
|
|
||||||
mouseScrolledDesc.getDescriptor(),
|
|
||||||
null,
|
|
||||||
new String[0]
|
|
||||||
);
|
|
||||||
var insns = mouseScrolledNode.instructions;
|
|
||||||
// ALOAD 0, load this
|
|
||||||
insns.add(new VarInsnNode(Opcodes.ALOAD, 0));
|
|
||||||
// DLOAD 1-4, load the 4 argument doubles. Note that since doubles are two entries wide we skip 2 each time.
|
|
||||||
insns.add(new VarInsnNode(Opcodes.DLOAD, 1));
|
|
||||||
insns.add(new VarInsnNode(Opcodes.DLOAD, 3));
|
|
||||||
insns.add(new VarInsnNode(Opcodes.DLOAD, 5));
|
|
||||||
insns.add(new VarInsnNode(Opcodes.DLOAD, 7));
|
|
||||||
// INVOKESPECIAL call super method
|
|
||||||
insns.add(new MethodInsnNode(Opcodes.INVOKESPECIAL, getTypeForClassName(Screen).getInternalName(), mouseScrolled, mouseScrolledDesc.getDescriptor()));
|
|
||||||
// IRETURN return int on stack (booleans are int at runtime)
|
|
||||||
insns.add(new InsnNode(Opcodes.IRETURN));
|
|
||||||
classNode.methods.add(mouseScrolledNode);
|
|
||||||
}
|
|
||||||
|
|
||||||
var insns = new InsnList();
|
/**
|
||||||
// ALOAD 0, load this
|
* Insert a handler that roughly inserts the following code at the beginning of the instruction list:
|
||||||
insns.add(new VarInsnNode(Opcodes.ALOAD, 0));
|
* <code><pre>
|
||||||
// DLOAD 1-4, load the 4 argument doubles. Note that since doubles are two entries wide we skip 2 each time.
|
* if (insertInvoke(insertLoads)) return true
|
||||||
insns.add(new VarInsnNode(Opcodes.DLOAD, 1));
|
* </pre></code>
|
||||||
insns.add(new VarInsnNode(Opcodes.DLOAD, 3));
|
*
|
||||||
insns.add(new VarInsnNode(Opcodes.DLOAD, 5));
|
* @param node The method node to prepend the instructions to
|
||||||
insns.add(new VarInsnNode(Opcodes.DLOAD, 7));
|
* @param insertLoads insert all the loads, including the {@code this} parameter
|
||||||
// INVOKEVIRTUAL call custom handler
|
* @param insertInvoke insert the invokevirtual/invokestatic call
|
||||||
insns.add(new MethodInsnNode(Opcodes.INVOKEVIRTUAL,
|
*/
|
||||||
getTypeForClassName(HandledScreen).getInternalName(),
|
void insertTrueHandler(MethodNode node,
|
||||||
"mouseScrolled_firmament",
|
Consumer<InsnList> insertLoads,
|
||||||
mouseScrolledDesc.getDescriptor()));
|
Consumer<InsnList> insertInvoke) {
|
||||||
// Create jump target (but not insert it yet)
|
|
||||||
var jumpIfFalse = new LabelNode();
|
var insns = new InsnList();
|
||||||
// IFEQ (if returned boolean == 0), jump to jumpIfFalse
|
insertLoads.accept(insns);
|
||||||
insns.add(new JumpInsnNode(Opcodes.IFEQ, jumpIfFalse));
|
insertInvoke.accept(insns);
|
||||||
// LDC 1 (as int, which is what booleans are at runtime)
|
// Create jump target (but not insert it yet)
|
||||||
insns.add(new LdcInsnNode(1));
|
var jumpIfFalse = new LabelNode();
|
||||||
// IRETURN return int on stack (booleans are int at runtime)
|
// IFEQ (if returned boolean == 0), jump to jumpIfFalse
|
||||||
insns.add(new InsnNode(Opcodes.IRETURN));
|
insns.add(new JumpInsnNode(Opcodes.IFEQ, jumpIfFalse));
|
||||||
insns.add(jumpIfFalse);
|
// LDC 1 (as int, which is what booleans are at runtime)
|
||||||
mouseScrolledNode.instructions.insert(insns);
|
insns.add(new LdcInsnNode(1));
|
||||||
}
|
// IRETURN return int on stack (booleans are int at runtime)
|
||||||
|
insns.add(new InsnNode(Opcodes.IRETURN));
|
||||||
|
insns.add(jumpIfFalse);
|
||||||
|
node.instructions.insert(insns);
|
||||||
|
}
|
||||||
|
|
||||||
|
void addKeyReleased(ClassNode classNode) {
|
||||||
|
var keyReleasedNode = findMethod(classNode, keyReleased, keyReleasedDesc);
|
||||||
|
if (keyReleasedNode == null) {
|
||||||
|
keyReleasedNode = new MethodNode(
|
||||||
|
Modifier.PUBLIC,
|
||||||
|
keyReleased,
|
||||||
|
keyReleasedDesc.getDescriptor(),
|
||||||
|
null,
|
||||||
|
new String[0]
|
||||||
|
);
|
||||||
|
var insns = keyReleasedNode.instructions;
|
||||||
|
// ALOAD 0, load this
|
||||||
|
insns.add(new VarInsnNode(Opcodes.ALOAD, 0));
|
||||||
|
// ILOAD 1-3, load args
|
||||||
|
insns.add(new VarInsnNode(Opcodes.ILOAD, 1));
|
||||||
|
insns.add(new VarInsnNode(Opcodes.ILOAD, 2));
|
||||||
|
insns.add(new VarInsnNode(Opcodes.ILOAD, 3));
|
||||||
|
// INVOKESPECIAL call super method
|
||||||
|
insns.add(new MethodInsnNode(Opcodes.INVOKESPECIAL, getTypeForClassName(Screen).getInternalName(),
|
||||||
|
keyReleased, keyReleasedDesc.getDescriptor()));
|
||||||
|
// IRETURN return int on stack (booleans are int at runtime)
|
||||||
|
insns.add(new InsnNode(Opcodes.IRETURN));
|
||||||
|
classNode.methods.add(keyReleasedNode);
|
||||||
|
}
|
||||||
|
insertTrueHandler(keyReleasedNode, insns -> {
|
||||||
|
// ALOAD 0, load this
|
||||||
|
insns.add(new VarInsnNode(Opcodes.ALOAD, 0));
|
||||||
|
// ILOAD 1-3, load args
|
||||||
|
insns.add(new VarInsnNode(Opcodes.ILOAD, 1));
|
||||||
|
insns.add(new VarInsnNode(Opcodes.ILOAD, 2));
|
||||||
|
insns.add(new VarInsnNode(Opcodes.ILOAD, 3));
|
||||||
|
}, insns -> {
|
||||||
|
// INVOKEVIRTUAL call custom handler
|
||||||
|
insns.add(new MethodInsnNode(Opcodes.INVOKEVIRTUAL,
|
||||||
|
getTypeForClassName(HandledScreen).getInternalName(),
|
||||||
|
"keyReleased_firmament",
|
||||||
|
keyReleasedDesc.getDescriptor()));
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
void addMouseScroll(ClassNode classNode) {
|
||||||
|
MethodNode mouseScrolledNode = findMethod(classNode, mouseScrolled, mouseScrolledDesc);
|
||||||
|
if (mouseScrolledNode == null) {
|
||||||
|
mouseScrolledNode = new MethodNode(
|
||||||
|
Modifier.PUBLIC,
|
||||||
|
mouseScrolled,
|
||||||
|
mouseScrolledDesc.getDescriptor(),
|
||||||
|
null,
|
||||||
|
new String[0]
|
||||||
|
);
|
||||||
|
var insns = mouseScrolledNode.instructions;
|
||||||
|
// ALOAD 0, load this
|
||||||
|
insns.add(new VarInsnNode(Opcodes.ALOAD, 0));
|
||||||
|
// DLOAD 1-4, load the 4 argument doubles. Note that since doubles are two entries wide we skip 2 each time.
|
||||||
|
insns.add(new VarInsnNode(Opcodes.DLOAD, 1));
|
||||||
|
insns.add(new VarInsnNode(Opcodes.DLOAD, 3));
|
||||||
|
insns.add(new VarInsnNode(Opcodes.DLOAD, 5));
|
||||||
|
insns.add(new VarInsnNode(Opcodes.DLOAD, 7));
|
||||||
|
// INVOKESPECIAL call super method
|
||||||
|
insns.add(new MethodInsnNode(Opcodes.INVOKESPECIAL, getTypeForClassName(Screen).getInternalName(), mouseScrolled, mouseScrolledDesc.getDescriptor()));
|
||||||
|
// IRETURN return int on stack (booleans are int at runtime)
|
||||||
|
insns.add(new InsnNode(Opcodes.IRETURN));
|
||||||
|
classNode.methods.add(mouseScrolledNode);
|
||||||
|
}
|
||||||
|
|
||||||
|
insertTrueHandler(mouseScrolledNode, insns -> {
|
||||||
|
// ALOAD 0, load this
|
||||||
|
insns.add(new VarInsnNode(Opcodes.ALOAD, 0));
|
||||||
|
// DLOAD 1-4, load the 4 argument doubles. Note that since doubles are two entries wide we skip 2 each time.
|
||||||
|
insns.add(new VarInsnNode(Opcodes.DLOAD, 1));
|
||||||
|
insns.add(new VarInsnNode(Opcodes.DLOAD, 3));
|
||||||
|
insns.add(new VarInsnNode(Opcodes.DLOAD, 5));
|
||||||
|
insns.add(new VarInsnNode(Opcodes.DLOAD, 7));
|
||||||
|
}, insns -> {
|
||||||
|
// INVOKEVIRTUAL call custom handler
|
||||||
|
insns.add(new MethodInsnNode(Opcodes.INVOKEVIRTUAL,
|
||||||
|
getTypeForClassName(HandledScreen).getInternalName(),
|
||||||
|
"mouseScrolled_firmament",
|
||||||
|
mouseScrolledDesc.getDescriptor()));
|
||||||
|
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -24,77 +24,81 @@ import org.spongepowered.asm.mixin.injection.callback.LocalCapture;
|
|||||||
@Mixin(HandledScreen.class)
|
@Mixin(HandledScreen.class)
|
||||||
public abstract class MixinHandledScreen<T extends ScreenHandler> {
|
public abstract class MixinHandledScreen<T extends ScreenHandler> {
|
||||||
|
|
||||||
@Shadow
|
@Shadow
|
||||||
@Final
|
@Final
|
||||||
protected T handler;
|
protected T handler;
|
||||||
|
|
||||||
@Shadow
|
@Shadow
|
||||||
public abstract T getScreenHandler();
|
public abstract T getScreenHandler();
|
||||||
|
|
||||||
@Shadow
|
@Shadow
|
||||||
protected int y;
|
protected int y;
|
||||||
@Shadow
|
@Shadow
|
||||||
protected int x;
|
protected int x;
|
||||||
@Unique
|
@Unique
|
||||||
PlayerInventory playerInventory;
|
PlayerInventory playerInventory;
|
||||||
|
|
||||||
@Inject(method = "<init>", at = @At("TAIL"))
|
@Inject(method = "<init>", at = @At("TAIL"))
|
||||||
public void savePlayerInventory(ScreenHandler handler, PlayerInventory inventory, Text title, CallbackInfo ci) {
|
public void savePlayerInventory(ScreenHandler handler, PlayerInventory inventory, Text title, CallbackInfo ci) {
|
||||||
this.playerInventory = inventory;
|
this.playerInventory = inventory;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Inject(method = "keyPressed", at = @At(value = "INVOKE", target = "Lnet/minecraft/client/gui/screen/ingame/HandledScreen;handleHotbarKeyPressed(II)Z", shift = At.Shift.BEFORE), cancellable = true)
|
@Inject(method = "keyPressed", at = @At(value = "INVOKE", target = "Lnet/minecraft/client/gui/screen/ingame/HandledScreen;handleHotbarKeyPressed(II)Z", shift = At.Shift.BEFORE), cancellable = true)
|
||||||
public void onKeyPressed(int keyCode, int scanCode, int modifiers, CallbackInfoReturnable<Boolean> cir) {
|
public void onKeyPressed(int keyCode, int scanCode, int modifiers, CallbackInfoReturnable<Boolean> cir) {
|
||||||
if (HandledScreenKeyPressedEvent.Companion.publish(new HandledScreenKeyPressedEvent((HandledScreen<?>) (Object) this, keyCode, scanCode, modifiers)).getCancelled()) {
|
if (HandledScreenKeyPressedEvent.Companion.publish(new HandledScreenKeyPressedEvent((HandledScreen<?>) (Object) this, keyCode, scanCode, modifiers)).getCancelled()) {
|
||||||
cir.setReturnValue(true);
|
cir.setReturnValue(true);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@Inject(method = "mouseClicked", at = @At("HEAD"), cancellable = true)
|
@Inject(method = "mouseClicked", at = @At("HEAD"), cancellable = true)
|
||||||
public void onMouseClicked(double mouseX, double mouseY, int button, CallbackInfoReturnable<Boolean> cir) {
|
public void onMouseClicked(double mouseX, double mouseY, int button, CallbackInfoReturnable<Boolean> cir) {
|
||||||
if (HandledScreenClickEvent.Companion.publish(new HandledScreenClickEvent((HandledScreen<?>) (Object) this, mouseX, mouseY, button)).getCancelled()) {
|
if (HandledScreenClickEvent.Companion.publish(new HandledScreenClickEvent((HandledScreen<?>) (Object) this, mouseX, mouseY, button)).getCancelled()) {
|
||||||
cir.setReturnValue(true);
|
cir.setReturnValue(true);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@Inject(method = "render", at = @At(value = "INVOKE", target = "Lnet/minecraft/client/gui/screen/ingame/HandledScreen;drawForeground(Lnet/minecraft/client/gui/DrawContext;II)V", shift = At.Shift.AFTER))
|
boolean keyReleased_firmament(int keyCode, int scanCode, int modifiers) {
|
||||||
public void onAfterRenderForeground(DrawContext context, int mouseX, int mouseY, float delta, CallbackInfo ci) {
|
return HandledScreenKeyReleasedEvent.Companion.publish(new HandledScreenKeyReleasedEvent((HandledScreen<?>) (Object) this, keyCode, scanCode, modifiers)).getCancelled();
|
||||||
context.getMatrices().push();
|
}
|
||||||
context.getMatrices().translate(-x, -y, 0);
|
|
||||||
HandledScreenForegroundEvent.Companion.publish(new HandledScreenForegroundEvent((HandledScreen<?>) (Object) this, context, mouseX, mouseY, delta));
|
|
||||||
context.getMatrices().pop();
|
|
||||||
}
|
|
||||||
|
|
||||||
@Inject(method = "onMouseClick(Lnet/minecraft/screen/slot/Slot;IILnet/minecraft/screen/slot/SlotActionType;)V", at = @At("HEAD"), cancellable = true)
|
@Inject(method = "render", at = @At(value = "INVOKE", target = "Lnet/minecraft/client/gui/screen/ingame/HandledScreen;drawForeground(Lnet/minecraft/client/gui/DrawContext;II)V", shift = At.Shift.AFTER))
|
||||||
public void onMouseClickedSlot(Slot slot, int slotId, int button, SlotActionType actionType, CallbackInfo ci) {
|
public void onAfterRenderForeground(DrawContext context, int mouseX, int mouseY, float delta, CallbackInfo ci) {
|
||||||
if (slotId == -999 && getScreenHandler() != null && actionType == SlotActionType.PICKUP) { // -999 is code for "clicked outside the main window"
|
context.getMatrices().push();
|
||||||
ItemStack cursorStack = getScreenHandler().getCursorStack();
|
context.getMatrices().translate(-x, -y, 0);
|
||||||
if (cursorStack != null && IsSlotProtectedEvent.shouldBlockInteraction(slot, SlotActionType.THROW, cursorStack)) {
|
HandledScreenForegroundEvent.Companion.publish(new HandledScreenForegroundEvent((HandledScreen<?>) (Object) this, context, mouseX, mouseY, delta));
|
||||||
ci.cancel();
|
context.getMatrices().pop();
|
||||||
return;
|
}
|
||||||
}
|
|
||||||
}
|
@Inject(method = "onMouseClick(Lnet/minecraft/screen/slot/Slot;IILnet/minecraft/screen/slot/SlotActionType;)V", at = @At("HEAD"), cancellable = true)
|
||||||
if (IsSlotProtectedEvent.shouldBlockInteraction(slot, actionType)) {
|
public void onMouseClickedSlot(Slot slot, int slotId, int button, SlotActionType actionType, CallbackInfo ci) {
|
||||||
ci.cancel();
|
if (slotId == -999 && getScreenHandler() != null && actionType == SlotActionType.PICKUP) { // -999 is code for "clicked outside the main window"
|
||||||
return;
|
ItemStack cursorStack = getScreenHandler().getCursorStack();
|
||||||
}
|
if (cursorStack != null && IsSlotProtectedEvent.shouldBlockInteraction(slot, SlotActionType.THROW, cursorStack)) {
|
||||||
if (actionType == SlotActionType.SWAP && 0 <= button && button < 9) {
|
ci.cancel();
|
||||||
if (IsSlotProtectedEvent.shouldBlockInteraction(new Slot(playerInventory, button, 0, 0), actionType)) {
|
return;
|
||||||
ci.cancel();
|
}
|
||||||
}
|
}
|
||||||
}
|
if (IsSlotProtectedEvent.shouldBlockInteraction(slot, actionType)) {
|
||||||
}
|
ci.cancel();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (actionType == SlotActionType.SWAP && 0 <= button && button < 9) {
|
||||||
|
if (IsSlotProtectedEvent.shouldBlockInteraction(new Slot(playerInventory, button, 0, 0), actionType)) {
|
||||||
|
ci.cancel();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
@Inject(method = "render", at = @At(value = "INVOKE", target = "Lnet/minecraft/client/gui/screen/ingame/HandledScreen;drawSlot(Lnet/minecraft/client/gui/DrawContext;Lnet/minecraft/screen/slot/Slot;)V", shift = At.Shift.AFTER), locals = LocalCapture.CAPTURE_FAILHARD)
|
@Inject(method = "render", at = @At(value = "INVOKE", target = "Lnet/minecraft/client/gui/screen/ingame/HandledScreen;drawSlot(Lnet/minecraft/client/gui/DrawContext;Lnet/minecraft/screen/slot/Slot;)V", shift = At.Shift.AFTER), locals = LocalCapture.CAPTURE_FAILHARD)
|
||||||
public void onAfterDrawSlot(DrawContext context, int mouseX, int mouseY, float delta, CallbackInfo ci, int i, int j, int k, Slot slot) {
|
public void onAfterDrawSlot(DrawContext context, int mouseX, int mouseY, float delta, CallbackInfo ci, int i, int j, int k, Slot slot) {
|
||||||
SlotRenderEvents.After event = new SlotRenderEvents.After(context, slot, mouseX, mouseY, delta);
|
SlotRenderEvents.After event = new SlotRenderEvents.After(context, slot, mouseX, mouseY, delta);
|
||||||
SlotRenderEvents.After.Companion.publish(event);
|
SlotRenderEvents.After.Companion.publish(event);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Inject(method = "render", at = @At(value = "INVOKE", target = "Lnet/minecraft/client/gui/screen/ingame/HandledScreen;drawSlot(Lnet/minecraft/client/gui/DrawContext;Lnet/minecraft/screen/slot/Slot;)V", shift = At.Shift.BEFORE), locals = LocalCapture.CAPTURE_FAILHARD)
|
@Inject(method = "render", at = @At(value = "INVOKE", target = "Lnet/minecraft/client/gui/screen/ingame/HandledScreen;drawSlot(Lnet/minecraft/client/gui/DrawContext;Lnet/minecraft/screen/slot/Slot;)V", shift = At.Shift.BEFORE), locals = LocalCapture.CAPTURE_FAILHARD)
|
||||||
public void onBeforeDrawSlot(DrawContext context, int mouseX, int mouseY, float delta, CallbackInfo ci, int i, int j, int k, Slot slot) {
|
public void onBeforeDrawSlot(DrawContext context, int mouseX, int mouseY, float delta, CallbackInfo ci, int i, int j, int k, Slot slot) {
|
||||||
SlotRenderEvents.Before event = new SlotRenderEvents.Before(context, slot, mouseX, mouseY, delta);
|
SlotRenderEvents.Before event = new SlotRenderEvents.Before(context, slot, mouseX, mouseY, delta);
|
||||||
SlotRenderEvents.Before.Companion.publish(event);
|
SlotRenderEvents.Before.Companion.publish(event);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,24 +1,38 @@
|
|||||||
|
|
||||||
|
|
||||||
package moe.nea.firmament.events
|
package moe.nea.firmament.events
|
||||||
|
|
||||||
import net.minecraft.client.gui.screen.ingame.HandledScreen
|
import net.minecraft.client.gui.screen.ingame.HandledScreen
|
||||||
import net.minecraft.client.option.KeyBinding
|
import net.minecraft.client.option.KeyBinding
|
||||||
import moe.nea.firmament.keybindings.IKeyBinding
|
import moe.nea.firmament.keybindings.IKeyBinding
|
||||||
|
|
||||||
data class HandledScreenKeyPressedEvent(
|
sealed interface HandledScreenKeyEvent {
|
||||||
val screen: HandledScreen<*>,
|
val screen: HandledScreen<*>
|
||||||
val keyCode: Int,
|
val keyCode: Int
|
||||||
val scanCode: Int,
|
val scanCode: Int
|
||||||
val modifiers: Int
|
val modifiers: Int
|
||||||
) : FirmamentEvent.Cancellable() {
|
|
||||||
companion object : FirmamentEventBus<HandledScreenKeyPressedEvent>()
|
|
||||||
|
|
||||||
fun matches(keyBinding: KeyBinding): Boolean {
|
fun matches(keyBinding: KeyBinding): Boolean {
|
||||||
return matches(IKeyBinding.minecraft(keyBinding))
|
return matches(IKeyBinding.minecraft(keyBinding))
|
||||||
}
|
}
|
||||||
|
|
||||||
fun matches(keyBinding: IKeyBinding): Boolean {
|
fun matches(keyBinding: IKeyBinding): Boolean {
|
||||||
return keyBinding.matches(keyCode, scanCode, modifiers)
|
return keyBinding.matches(keyCode, scanCode, modifiers)
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
data class HandledScreenKeyPressedEvent(
|
||||||
|
override val screen: HandledScreen<*>,
|
||||||
|
override val keyCode: Int,
|
||||||
|
override val scanCode: Int,
|
||||||
|
override val modifiers: Int
|
||||||
|
) : FirmamentEvent.Cancellable(), HandledScreenKeyEvent {
|
||||||
|
companion object : FirmamentEventBus<HandledScreenKeyPressedEvent>()
|
||||||
|
}
|
||||||
|
|
||||||
|
data class HandledScreenKeyReleasedEvent(
|
||||||
|
override val screen: HandledScreen<*>,
|
||||||
|
override val keyCode: Int,
|
||||||
|
override val scanCode: Int,
|
||||||
|
override val modifiers: Int
|
||||||
|
) : FirmamentEvent.Cancellable(), HandledScreenKeyEvent {
|
||||||
|
companion object : FirmamentEventBus<HandledScreenKeyReleasedEvent>()
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,5 +1,3 @@
|
|||||||
|
|
||||||
|
|
||||||
@file:UseSerializers(DashlessUUIDSerializer::class)
|
@file:UseSerializers(DashlessUUIDSerializer::class)
|
||||||
|
|
||||||
package moe.nea.firmament.features.inventory
|
package moe.nea.firmament.features.inventory
|
||||||
@@ -13,11 +11,15 @@ import kotlinx.serialization.serializer
|
|||||||
import net.minecraft.client.gui.screen.ingame.HandledScreen
|
import net.minecraft.client.gui.screen.ingame.HandledScreen
|
||||||
import net.minecraft.entity.player.PlayerInventory
|
import net.minecraft.entity.player.PlayerInventory
|
||||||
import net.minecraft.screen.GenericContainerScreenHandler
|
import net.minecraft.screen.GenericContainerScreenHandler
|
||||||
|
import net.minecraft.screen.slot.Slot
|
||||||
import net.minecraft.screen.slot.SlotActionType
|
import net.minecraft.screen.slot.SlotActionType
|
||||||
import net.minecraft.util.Identifier
|
import net.minecraft.util.Identifier
|
||||||
import moe.nea.firmament.annotations.Subscribe
|
import moe.nea.firmament.annotations.Subscribe
|
||||||
|
import moe.nea.firmament.events.HandledScreenForegroundEvent
|
||||||
import moe.nea.firmament.events.HandledScreenKeyPressedEvent
|
import moe.nea.firmament.events.HandledScreenKeyPressedEvent
|
||||||
|
import moe.nea.firmament.events.HandledScreenKeyReleasedEvent
|
||||||
import moe.nea.firmament.events.IsSlotProtectedEvent
|
import moe.nea.firmament.events.IsSlotProtectedEvent
|
||||||
|
import moe.nea.firmament.events.ScreenChangeEvent
|
||||||
import moe.nea.firmament.events.SlotRenderEvents
|
import moe.nea.firmament.events.SlotRenderEvents
|
||||||
import moe.nea.firmament.features.FirmamentFeature
|
import moe.nea.firmament.features.FirmamentFeature
|
||||||
import moe.nea.firmament.gui.config.ManagedConfig
|
import moe.nea.firmament.gui.config.ManagedConfig
|
||||||
@@ -28,176 +30,325 @@ import moe.nea.firmament.util.MC
|
|||||||
import moe.nea.firmament.util.SBData
|
import moe.nea.firmament.util.SBData
|
||||||
import moe.nea.firmament.util.SkyBlockIsland
|
import moe.nea.firmament.util.SkyBlockIsland
|
||||||
import moe.nea.firmament.util.data.ProfileSpecificDataHolder
|
import moe.nea.firmament.util.data.ProfileSpecificDataHolder
|
||||||
|
import moe.nea.firmament.util.json.DashlessUUIDSerializer
|
||||||
|
import moe.nea.firmament.util.mc.ScreenUtil.getSlotByIndex
|
||||||
|
import moe.nea.firmament.util.mc.SlotUtils.swapWithHotBar
|
||||||
import moe.nea.firmament.util.mc.displayNameAccordingToNbt
|
import moe.nea.firmament.util.mc.displayNameAccordingToNbt
|
||||||
import moe.nea.firmament.util.mc.loreAccordingToNbt
|
import moe.nea.firmament.util.mc.loreAccordingToNbt
|
||||||
import moe.nea.firmament.util.json.DashlessUUIDSerializer
|
import moe.nea.firmament.util.render.drawLine
|
||||||
import moe.nea.firmament.util.skyblockUUID
|
import moe.nea.firmament.util.skyblockUUID
|
||||||
import moe.nea.firmament.util.unformattedString
|
import moe.nea.firmament.util.unformattedString
|
||||||
|
|
||||||
object SlotLocking : FirmamentFeature {
|
object SlotLocking : FirmamentFeature {
|
||||||
override val identifier: String
|
override val identifier: String
|
||||||
get() = "slot-locking"
|
get() = "slot-locking"
|
||||||
|
|
||||||
@Serializable
|
@Serializable
|
||||||
data class Data(
|
data class Data(
|
||||||
val lockedSlots: MutableSet<Int> = mutableSetOf(),
|
val lockedSlots: MutableSet<Int> = mutableSetOf(),
|
||||||
val lockedSlotsRift: MutableSet<Int> = mutableSetOf(),
|
val lockedSlotsRift: MutableSet<Int> = mutableSetOf(),
|
||||||
|
val lockedUUIDs: MutableSet<UUID> = mutableSetOf(),
|
||||||
|
val boundSlots: MutableMap<Int, Int> = mutableMapOf()
|
||||||
|
)
|
||||||
|
|
||||||
val lockedUUIDs: MutableSet<UUID> = mutableSetOf(),
|
object TConfig : ManagedConfig(identifier, Category.INVENTORY) {
|
||||||
)
|
val lockSlot by keyBinding("lock") { GLFW.GLFW_KEY_L }
|
||||||
|
val lockUUID by keyBindingWithOutDefaultModifiers("lock-uuid") {
|
||||||
|
SavedKeyBinding(GLFW.GLFW_KEY_L, shift = true)
|
||||||
|
}
|
||||||
|
val slotBind by keyBinding("bind") { GLFW.GLFW_KEY_L }
|
||||||
|
val slotBindRequireShift by toggle("require-quick-move") { true }
|
||||||
|
}
|
||||||
|
|
||||||
object TConfig : ManagedConfig(identifier, Category.INVENTORY) {
|
override val config: TConfig
|
||||||
val lockSlot by keyBinding("lock") { GLFW.GLFW_KEY_L }
|
get() = TConfig
|
||||||
val lockUUID by keyBindingWithOutDefaultModifiers("lock-uuid") {
|
|
||||||
SavedKeyBinding(GLFW.GLFW_KEY_L, shift = true)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
override val config: TConfig
|
object DConfig : ProfileSpecificDataHolder<Data>(serializer(), "locked-slots", ::Data)
|
||||||
get() = TConfig
|
|
||||||
|
|
||||||
object DConfig : ProfileSpecificDataHolder<Data>(serializer(), "locked-slots", ::Data)
|
val lockedUUIDs get() = DConfig.data?.lockedUUIDs
|
||||||
|
|
||||||
val lockedUUIDs get() = DConfig.data?.lockedUUIDs
|
val lockedSlots
|
||||||
|
get() = when (SBData.skyblockLocation) {
|
||||||
|
SkyBlockIsland.RIFT -> DConfig.data?.lockedSlotsRift
|
||||||
|
null -> null
|
||||||
|
else -> DConfig.data?.lockedSlots
|
||||||
|
}
|
||||||
|
|
||||||
val lockedSlots
|
fun isSalvageScreen(screen: HandledScreen<*>?): Boolean {
|
||||||
get() = when (SBData.skyblockLocation) {
|
if (screen == null) return false
|
||||||
SkyBlockIsland.RIFT -> DConfig.data?.lockedSlotsRift
|
return screen.title.unformattedString.contains("Salvage Item")
|
||||||
null -> null
|
}
|
||||||
else -> DConfig.data?.lockedSlots
|
|
||||||
}
|
|
||||||
|
|
||||||
fun isSalvageScreen(screen: HandledScreen<*>?): Boolean {
|
fun isTradeScreen(screen: HandledScreen<*>?): Boolean {
|
||||||
if (screen == null) return false
|
if (screen == null) return false
|
||||||
return screen.title.unformattedString.contains("Salvage Item")
|
val handler = screen.screenHandler as? GenericContainerScreenHandler ?: return false
|
||||||
}
|
if (handler.inventory.size() < 9) return false
|
||||||
|
val middlePane = handler.inventory.getStack(handler.inventory.size() - 5)
|
||||||
|
if (middlePane == null) return false
|
||||||
|
return middlePane.displayNameAccordingToNbt?.unformattedString == "⇦ Your stuff"
|
||||||
|
}
|
||||||
|
|
||||||
fun isTradeScreen(screen: HandledScreen<*>?): Boolean {
|
fun isNpcShop(screen: HandledScreen<*>?): Boolean {
|
||||||
if (screen == null) return false
|
if (screen == null) return false
|
||||||
val handler = screen.screenHandler as? GenericContainerScreenHandler ?: return false
|
val handler = screen.screenHandler as? GenericContainerScreenHandler ?: return false
|
||||||
if (handler.inventory.size() < 9) return false
|
if (handler.inventory.size() < 9) return false
|
||||||
val middlePane = handler.inventory.getStack(handler.inventory.size() - 5)
|
val sellItem = handler.inventory.getStack(handler.inventory.size() - 5)
|
||||||
if (middlePane == null) return false
|
if (sellItem == null) return false
|
||||||
return middlePane.displayNameAccordingToNbt?.unformattedString == "⇦ Your stuff"
|
if (sellItem.displayNameAccordingToNbt?.unformattedString == "Sell Item") return true
|
||||||
}
|
val lore = sellItem.loreAccordingToNbt
|
||||||
|
return (lore.lastOrNull() ?: return false).unformattedString == "Click to buyback!"
|
||||||
|
}
|
||||||
|
|
||||||
fun isNpcShop(screen: HandledScreen<*>?): Boolean {
|
@Subscribe
|
||||||
if (screen == null) return false
|
fun onSalvageProtect(event: IsSlotProtectedEvent) {
|
||||||
val handler = screen.screenHandler as? GenericContainerScreenHandler ?: return false
|
if (event.slot == null) return
|
||||||
if (handler.inventory.size() < 9) return false
|
if (!event.slot.hasStack()) return
|
||||||
val sellItem = handler.inventory.getStack(handler.inventory.size() - 5)
|
if (event.slot.stack.displayNameAccordingToNbt?.unformattedString != "Salvage Items") return
|
||||||
if (sellItem == null) return false
|
val inv = event.slot.inventory
|
||||||
if (sellItem.displayNameAccordingToNbt?.unformattedString == "Sell Item") return true
|
var anyBlocked = false
|
||||||
val lore = sellItem.loreAccordingToNbt
|
for (i in 0 until event.slot.index) {
|
||||||
return (lore.lastOrNull() ?: return false).unformattedString == "Click to buyback!"
|
val stack = inv.getStack(i)
|
||||||
}
|
if (IsSlotProtectedEvent.shouldBlockInteraction(null, SlotActionType.THROW, stack))
|
||||||
|
anyBlocked = true
|
||||||
|
}
|
||||||
|
if (anyBlocked) {
|
||||||
|
event.protectSilent()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
@Subscribe
|
@Subscribe
|
||||||
fun onSalvageProtect(event: IsSlotProtectedEvent) {
|
fun onProtectUuidItems(event: IsSlotProtectedEvent) {
|
||||||
if (event.slot == null) return
|
val doesNotDeleteItem = event.actionType == SlotActionType.SWAP
|
||||||
if (!event.slot.hasStack()) return
|
|| event.actionType == SlotActionType.PICKUP
|
||||||
if (event.slot.stack.displayNameAccordingToNbt?.unformattedString != "Salvage Items") return
|
|| event.actionType == SlotActionType.QUICK_MOVE
|
||||||
val inv = event.slot.inventory
|
|| event.actionType == SlotActionType.QUICK_CRAFT
|
||||||
var anyBlocked = false
|
|| event.actionType == SlotActionType.CLONE
|
||||||
for (i in 0 until event.slot.index) {
|
|| event.actionType == SlotActionType.PICKUP_ALL
|
||||||
val stack = inv.getStack(i)
|
val isSellOrTradeScreen =
|
||||||
if (IsSlotProtectedEvent.shouldBlockInteraction(null, SlotActionType.THROW, stack))
|
isNpcShop(MC.handledScreen) || isTradeScreen(MC.handledScreen) || isSalvageScreen(MC.handledScreen)
|
||||||
anyBlocked = true
|
if ((!isSellOrTradeScreen || event.slot?.inventory !is PlayerInventory)
|
||||||
}
|
&& doesNotDeleteItem
|
||||||
if (anyBlocked) {
|
) return
|
||||||
event.protectSilent()
|
val stack = event.itemStack ?: return
|
||||||
}
|
val uuid = stack.skyblockUUID ?: return
|
||||||
}
|
if (uuid in (lockedUUIDs ?: return)) {
|
||||||
|
event.protect()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
@Subscribe
|
@Subscribe
|
||||||
fun onProtectUuidItems(event: IsSlotProtectedEvent) {
|
fun onProtectSlot(it: IsSlotProtectedEvent) {
|
||||||
val doesNotDeleteItem = event.actionType == SlotActionType.SWAP
|
if (it.slot != null && it.slot.inventory is PlayerInventory && it.slot.index in (lockedSlots ?: setOf())) {
|
||||||
|| event.actionType == SlotActionType.PICKUP
|
it.protect()
|
||||||
|| event.actionType == SlotActionType.QUICK_MOVE
|
}
|
||||||
|| event.actionType == SlotActionType.QUICK_CRAFT
|
}
|
||||||
|| event.actionType == SlotActionType.CLONE
|
|
||||||
|| event.actionType == SlotActionType.PICKUP_ALL
|
|
||||||
val isSellOrTradeScreen =
|
|
||||||
isNpcShop(MC.handledScreen) || isTradeScreen(MC.handledScreen) || isSalvageScreen(MC.handledScreen)
|
|
||||||
if ((!isSellOrTradeScreen || event.slot?.inventory !is PlayerInventory)
|
|
||||||
&& doesNotDeleteItem
|
|
||||||
) return
|
|
||||||
val stack = event.itemStack ?: return
|
|
||||||
val uuid = stack.skyblockUUID ?: return
|
|
||||||
if (uuid in (lockedUUIDs ?: return)) {
|
|
||||||
event.protect()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@Subscribe
|
@Subscribe
|
||||||
fun onProtectSlot(it: IsSlotProtectedEvent) {
|
fun onQuickMoveBoundSlot(it: IsSlotProtectedEvent) {
|
||||||
if (it.slot != null && it.slot.inventory is PlayerInventory && it.slot.index in (lockedSlots ?: setOf())) {
|
val boundSlots = DConfig.data?.boundSlots ?: mapOf()
|
||||||
it.protect()
|
val isValidAction =
|
||||||
}
|
it.actionType == SlotActionType.QUICK_MOVE || (it.actionType == SlotActionType.PICKUP && !TConfig.slotBindRequireShift)
|
||||||
}
|
if (!isValidAction) return
|
||||||
|
val handler = MC.handledScreen?.screenHandler ?: return
|
||||||
|
val slot = it.slot
|
||||||
|
if (slot != null && it.slot.inventory is PlayerInventory) {
|
||||||
|
val boundSlot = boundSlots.entries.find {
|
||||||
|
it.value == slot.index || it.key == slot.index
|
||||||
|
} ?: return
|
||||||
|
it.protectSilent()
|
||||||
|
val inventorySlot = MC.handledScreen?.getSlotByIndex(boundSlot.value, true)
|
||||||
|
inventorySlot?.swapWithHotBar(handler, boundSlot.key)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
@Subscribe
|
@Subscribe
|
||||||
fun onLockUUID(it: HandledScreenKeyPressedEvent) {
|
fun onLockUUID(it: HandledScreenKeyPressedEvent) {
|
||||||
if (!it.matches(TConfig.lockUUID)) return
|
if (!it.matches(TConfig.lockUUID)) return
|
||||||
val inventory = MC.handledScreen ?: return
|
val inventory = MC.handledScreen ?: return
|
||||||
inventory as AccessorHandledScreen
|
inventory as AccessorHandledScreen
|
||||||
|
|
||||||
val slot = inventory.focusedSlot_Firmament ?: return
|
val slot = inventory.focusedSlot_Firmament ?: return
|
||||||
val stack = slot.stack ?: return
|
val stack = slot.stack ?: return
|
||||||
val uuid = stack.skyblockUUID ?: return
|
val uuid = stack.skyblockUUID ?: return
|
||||||
val lockedUUIDs = lockedUUIDs ?: return
|
val lockedUUIDs = lockedUUIDs ?: return
|
||||||
if (uuid in lockedUUIDs) {
|
if (uuid in lockedUUIDs) {
|
||||||
lockedUUIDs.remove(uuid)
|
lockedUUIDs.remove(uuid)
|
||||||
} else {
|
} else {
|
||||||
lockedUUIDs.add(uuid)
|
lockedUUIDs.add(uuid)
|
||||||
}
|
}
|
||||||
DConfig.markDirty()
|
DConfig.markDirty()
|
||||||
CommonSoundEffects.playSuccess()
|
CommonSoundEffects.playSuccess()
|
||||||
it.cancel()
|
it.cancel()
|
||||||
}
|
}
|
||||||
|
|
||||||
@Subscribe
|
|
||||||
fun onLockSlot(it: HandledScreenKeyPressedEvent) {
|
|
||||||
if (!it.matches(TConfig.lockSlot)) return
|
|
||||||
val inventory = MC.handledScreen ?: return
|
|
||||||
inventory as AccessorHandledScreen
|
|
||||||
|
|
||||||
val slot = inventory.focusedSlot_Firmament ?: return
|
@Subscribe
|
||||||
val lockedSlots = lockedSlots ?: return
|
fun onLockSlotKeyRelease(it: HandledScreenKeyReleasedEvent) {
|
||||||
if (slot.inventory is PlayerInventory) {
|
val inventory = MC.handledScreen ?: return
|
||||||
if (slot.index in lockedSlots) {
|
inventory as AccessorHandledScreen
|
||||||
lockedSlots.remove(slot.index)
|
val slot = inventory.focusedSlot_Firmament
|
||||||
} else {
|
val storedSlot = storedLockingSlot ?: return
|
||||||
lockedSlots.add(slot.index)
|
|
||||||
}
|
|
||||||
DConfig.markDirty()
|
|
||||||
CommonSoundEffects.playSuccess()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@Subscribe
|
if (it.matches(TConfig.slotBind) && slot != storedSlot && slot != null && slot.isHotbar() != storedSlot.isHotbar()) {
|
||||||
fun onRenderSlotOverlay(it: SlotRenderEvents.After) {
|
storedLockingSlot = null
|
||||||
val isSlotLocked = it.slot.inventory is PlayerInventory && it.slot.index in (lockedSlots ?: setOf())
|
val hotBarSlot = if (slot.isHotbar()) slot else storedSlot
|
||||||
val isUUIDLocked = (it.slot.stack?.skyblockUUID) in (lockedUUIDs ?: setOf())
|
val invSlot = if (slot.isHotbar()) storedSlot else slot
|
||||||
if (isSlotLocked || isUUIDLocked) {
|
val boundSlots = DConfig.data?.boundSlots ?: return
|
||||||
RenderSystem.disableDepthTest()
|
lockedSlots?.remove(hotBarSlot.index)
|
||||||
it.context.drawSprite(
|
lockedSlots?.remove(invSlot.index)
|
||||||
it.slot.x, it.slot.y, 0,
|
boundSlots.entries.removeIf {
|
||||||
16, 16,
|
it.value == invSlot.index
|
||||||
MC.guiAtlasManager.getSprite(
|
}
|
||||||
when {
|
boundSlots[hotBarSlot.index] = invSlot.index
|
||||||
isSlotLocked ->
|
DConfig.markDirty()
|
||||||
(Identifier.of("firmament:slot_locked"))
|
CommonSoundEffects.playSuccess()
|
||||||
|
return
|
||||||
|
}
|
||||||
|
if (it.matches(TConfig.lockSlot) && slot == storedSlot) {
|
||||||
|
storedLockingSlot = null
|
||||||
|
toggleSlotLock(slot)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
if (it.matches(TConfig.slotBind)) {
|
||||||
|
storedLockingSlot = null
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
isUUIDLocked ->
|
@Subscribe
|
||||||
(Identifier.of("firmament:uuid_locked"))
|
fun onRenderAllBoundSlots(event: HandledScreenForegroundEvent) {
|
||||||
|
val boundSlots = DConfig.data?.boundSlots ?: return
|
||||||
|
fun findByIndex(index: Int) = event.screen.getSlotByIndex(index, true)
|
||||||
|
val accScreen = event.screen as AccessorHandledScreen
|
||||||
|
val sx = accScreen.x_Firmament
|
||||||
|
val sy = accScreen.y_Firmament
|
||||||
|
boundSlots.entries.forEach {
|
||||||
|
val hotbarSlot = findByIndex(it.key) ?: return@forEach
|
||||||
|
val inventorySlot = findByIndex(it.value) ?: return@forEach
|
||||||
|
|
||||||
else ->
|
val (hotX, hotY) = hotbarSlot.lineCenter()
|
||||||
error("unreachable")
|
val (invX, invY) = inventorySlot.lineCenter()
|
||||||
}
|
event.context.drawLine(
|
||||||
)
|
invX + sx, invY + sy,
|
||||||
)
|
hotX + sx, hotY + sy,
|
||||||
RenderSystem.enableDepthTest()
|
me.shedaniel.math.Color.ofOpaque(0x00FF00)
|
||||||
}
|
)
|
||||||
}
|
event.context.drawBorder(hotbarSlot.x + sx,
|
||||||
|
hotbarSlot.y + sy,
|
||||||
|
16, 16, 0xFF00FF00u.toInt())
|
||||||
|
event.context.drawBorder(inventorySlot.x + sx,
|
||||||
|
inventorySlot.y + sy,
|
||||||
|
16, 16, 0xFF00FF00u.toInt())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Subscribe
|
||||||
|
fun onRenderCurrentDraggingSlot(event: HandledScreenForegroundEvent) {
|
||||||
|
val draggingSlot = storedLockingSlot ?: return
|
||||||
|
val accScreen = event.screen as AccessorHandledScreen
|
||||||
|
val hoveredSlot = accScreen.focusedSlot_Firmament
|
||||||
|
?.takeIf { it.inventory is PlayerInventory }
|
||||||
|
?.takeIf { it == draggingSlot || it.isHotbar() != draggingSlot.isHotbar() }
|
||||||
|
val sx = accScreen.x_Firmament
|
||||||
|
val sy = accScreen.y_Firmament
|
||||||
|
val (borderX, borderY) = draggingSlot.lineCenter()
|
||||||
|
event.context.drawBorder(draggingSlot.x + sx, draggingSlot.y + sy, 16, 16, 0xFF00FF00u.toInt())
|
||||||
|
if (hoveredSlot == null) {
|
||||||
|
event.context.drawLine(
|
||||||
|
borderX + sx, borderY + sy,
|
||||||
|
event.mouseX, event.mouseY,
|
||||||
|
me.shedaniel.math.Color.ofOpaque(0x00FF00)
|
||||||
|
)
|
||||||
|
} else if (hoveredSlot != draggingSlot) {
|
||||||
|
val (hovX, hovY) = hoveredSlot.lineCenter()
|
||||||
|
event.context.drawLine(
|
||||||
|
borderX + sx, borderY + sy,
|
||||||
|
hovX + sx, hovY + sy,
|
||||||
|
me.shedaniel.math.Color.ofOpaque(0x00FF00)
|
||||||
|
)
|
||||||
|
event.context.drawBorder(hoveredSlot.x + sx,
|
||||||
|
hoveredSlot.y + sy,
|
||||||
|
16, 16, 0xFF00FF00u.toInt())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fun Slot.lineCenter(): Pair<Int, Int> {
|
||||||
|
return if (isHotbar()) {
|
||||||
|
x + 9 to y
|
||||||
|
} else {
|
||||||
|
x + 9 to y + 17
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
fun Slot.isHotbar(): Boolean {
|
||||||
|
return index < 9
|
||||||
|
}
|
||||||
|
|
||||||
|
@Subscribe
|
||||||
|
fun onScreenChange(event: ScreenChangeEvent) {
|
||||||
|
storedLockingSlot = null
|
||||||
|
}
|
||||||
|
|
||||||
|
var storedLockingSlot: Slot? = null
|
||||||
|
|
||||||
|
fun toggleSlotLock(slot: Slot) {
|
||||||
|
val lockedSlots = lockedSlots ?: return
|
||||||
|
val boundSlots = DConfig.data?.boundSlots ?: mutableMapOf()
|
||||||
|
if (slot.inventory is PlayerInventory) {
|
||||||
|
if (boundSlots.entries.removeIf {
|
||||||
|
it.value == slot.index || it.key == slot.index
|
||||||
|
}) {
|
||||||
|
// intentionally do nothing
|
||||||
|
} else if (slot.index in lockedSlots) {
|
||||||
|
lockedSlots.remove(slot.index)
|
||||||
|
} else {
|
||||||
|
lockedSlots.add(slot.index)
|
||||||
|
}
|
||||||
|
DConfig.markDirty()
|
||||||
|
CommonSoundEffects.playSuccess()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Subscribe
|
||||||
|
fun onLockSlot(it: HandledScreenKeyPressedEvent) {
|
||||||
|
val inventory = MC.handledScreen ?: return
|
||||||
|
inventory as AccessorHandledScreen
|
||||||
|
|
||||||
|
val slot = inventory.focusedSlot_Firmament ?: return
|
||||||
|
if (slot.inventory !is PlayerInventory) return
|
||||||
|
if (it.matches(TConfig.slotBind)) {
|
||||||
|
storedLockingSlot = storedLockingSlot ?: slot
|
||||||
|
return
|
||||||
|
}
|
||||||
|
if (!it.matches(TConfig.lockSlot)) {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
toggleSlotLock(slot)
|
||||||
|
}
|
||||||
|
|
||||||
|
@Subscribe
|
||||||
|
fun onRenderSlotOverlay(it: SlotRenderEvents.After) {
|
||||||
|
val isSlotLocked = it.slot.inventory is PlayerInventory && it.slot.index in (lockedSlots ?: setOf())
|
||||||
|
val isUUIDLocked = (it.slot.stack?.skyblockUUID) in (lockedUUIDs ?: setOf())
|
||||||
|
if (isSlotLocked || isUUIDLocked) {
|
||||||
|
RenderSystem.disableDepthTest()
|
||||||
|
it.context.drawSprite(
|
||||||
|
it.slot.x, it.slot.y, 0,
|
||||||
|
16, 16,
|
||||||
|
MC.guiAtlasManager.getSprite(
|
||||||
|
when {
|
||||||
|
isSlotLocked ->
|
||||||
|
(Identifier.of("firmament:slot_locked"))
|
||||||
|
|
||||||
|
isUUIDLocked ->
|
||||||
|
(Identifier.of("firmament:uuid_locked"))
|
||||||
|
|
||||||
|
else ->
|
||||||
|
error("unreachable")
|
||||||
|
}
|
||||||
|
)
|
||||||
|
)
|
||||||
|
RenderSystem.enableDepthTest()
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -29,6 +29,7 @@ import moe.nea.firmament.util.TimeMark
|
|||||||
import moe.nea.firmament.util.customgui.CustomGui
|
import moe.nea.firmament.util.customgui.CustomGui
|
||||||
import moe.nea.firmament.util.customgui.customGui
|
import moe.nea.firmament.util.customgui.customGui
|
||||||
import moe.nea.firmament.util.mc.CommonTextures
|
import moe.nea.firmament.util.mc.CommonTextures
|
||||||
|
import moe.nea.firmament.util.mc.SlotUtils.clickRightMouseButton
|
||||||
import moe.nea.firmament.util.mc.displayNameAccordingToNbt
|
import moe.nea.firmament.util.mc.displayNameAccordingToNbt
|
||||||
import moe.nea.firmament.util.unformattedString
|
import moe.nea.firmament.util.unformattedString
|
||||||
import moe.nea.firmament.util.useMatch
|
import moe.nea.firmament.util.useMatch
|
||||||
@@ -103,26 +104,6 @@ object HotmPresets {
|
|||||||
var hasScrolled = false
|
var hasScrolled = false
|
||||||
var hasAll = false
|
var hasAll = false
|
||||||
|
|
||||||
fun Slot.clickMiddleMouseButton(handler: ScreenHandler) {
|
|
||||||
MC.interactionManager?.clickSlot(
|
|
||||||
handler.syncId,
|
|
||||||
this.id,
|
|
||||||
2,
|
|
||||||
SlotActionType.CLONE,
|
|
||||||
MC.player
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
fun Slot.clickRightMouseButton(handler: ScreenHandler) {
|
|
||||||
MC.interactionManager?.clickSlot(
|
|
||||||
handler.syncId,
|
|
||||||
this.id,
|
|
||||||
1,
|
|
||||||
SlotActionType.PICKUP,
|
|
||||||
MC.player
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
override fun mouseClick(mouseX: Double, mouseY: Double, button: Int): Boolean {
|
override fun mouseClick(mouseX: Double, mouseY: Double, button: Int): Boolean {
|
||||||
if (!hasScrolled) {
|
if (!hasScrolled) {
|
||||||
val slot = screen.screenHandler.getSlot(8)
|
val slot = screen.screenHandler.getSlot(8)
|
||||||
|
|||||||
26
src/main/kotlin/util/mc/ScreenUtil.kt
Normal file
26
src/main/kotlin/util/mc/ScreenUtil.kt
Normal file
@@ -0,0 +1,26 @@
|
|||||||
|
package moe.nea.firmament.util.mc
|
||||||
|
|
||||||
|
import net.minecraft.client.gui.screen.Screen
|
||||||
|
import net.minecraft.client.gui.screen.ingame.HandledScreen
|
||||||
|
import net.minecraft.entity.player.PlayerInventory
|
||||||
|
import net.minecraft.screen.slot.Slot
|
||||||
|
|
||||||
|
object ScreenUtil {
|
||||||
|
private var lastScreen: Screen? = null
|
||||||
|
private var slotsByIndex: Map<SlotIndex, Slot> = mapOf()
|
||||||
|
|
||||||
|
data class SlotIndex(val index: Int, val isPlayerInventory: Boolean)
|
||||||
|
|
||||||
|
fun Screen.getSlotsByIndex(): Map<SlotIndex, Slot> {
|
||||||
|
if (this !is HandledScreen<*>) return mapOf()
|
||||||
|
if (lastScreen === this) return slotsByIndex
|
||||||
|
lastScreen = this
|
||||||
|
slotsByIndex = this.screenHandler.slots.associate {
|
||||||
|
SlotIndex(it.index, it.inventory is PlayerInventory) to it
|
||||||
|
}
|
||||||
|
return slotsByIndex
|
||||||
|
}
|
||||||
|
|
||||||
|
fun Screen.getSlotByIndex( index: Int, isPlayerInventory: Boolean): Slot? =
|
||||||
|
getSlotsByIndex()[SlotIndex(index, isPlayerInventory)]
|
||||||
|
}
|
||||||
35
src/main/kotlin/util/mc/SlotUtils.kt
Normal file
35
src/main/kotlin/util/mc/SlotUtils.kt
Normal file
@@ -0,0 +1,35 @@
|
|||||||
|
package moe.nea.firmament.util.mc
|
||||||
|
|
||||||
|
import net.minecraft.screen.ScreenHandler
|
||||||
|
import net.minecraft.screen.slot.Slot
|
||||||
|
import net.minecraft.screen.slot.SlotActionType
|
||||||
|
import moe.nea.firmament.util.MC
|
||||||
|
|
||||||
|
object SlotUtils {
|
||||||
|
fun Slot.clickMiddleMouseButton(handler: ScreenHandler) {
|
||||||
|
MC.interactionManager?.clickSlot(
|
||||||
|
handler.syncId,
|
||||||
|
this.id,
|
||||||
|
2,
|
||||||
|
SlotActionType.CLONE,
|
||||||
|
MC.player
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
fun Slot.swapWithHotBar(handler: ScreenHandler, hotbarIndex: Int) {
|
||||||
|
MC.interactionManager?.clickSlot(
|
||||||
|
handler.syncId, this.id,
|
||||||
|
hotbarIndex, SlotActionType.SWAP,
|
||||||
|
MC.player)
|
||||||
|
}
|
||||||
|
|
||||||
|
fun Slot.clickRightMouseButton(handler: ScreenHandler) {
|
||||||
|
MC.interactionManager?.clickSlot(
|
||||||
|
handler.syncId,
|
||||||
|
this.id,
|
||||||
|
1,
|
||||||
|
SlotActionType.PICKUP,
|
||||||
|
MC.player
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -1,8 +1,27 @@
|
|||||||
package moe.nea.firmament.util.render
|
package moe.nea.firmament.util.render
|
||||||
|
|
||||||
|
import com.mojang.blaze3d.systems.RenderSystem
|
||||||
|
import me.shedaniel.math.Color
|
||||||
import org.joml.Matrix4f
|
import org.joml.Matrix4f
|
||||||
import net.minecraft.client.gui.DrawContext
|
import net.minecraft.client.gui.DrawContext
|
||||||
|
import moe.nea.firmament.util.MC
|
||||||
|
|
||||||
fun DrawContext.isUntranslatedGuiDrawContext(): Boolean {
|
fun DrawContext.isUntranslatedGuiDrawContext(): Boolean {
|
||||||
return (matrices.peek().positionMatrix.properties() and Matrix4f.PROPERTY_IDENTITY.toInt()) != 0
|
return (matrices.peek().positionMatrix.properties() and Matrix4f.PROPERTY_IDENTITY.toInt()) != 0
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fun DrawContext.drawLine(fromX: Int, fromY: Int, toX: Int, toY: Int, color: Color) {
|
||||||
|
// TODO: push scissors
|
||||||
|
if (toY < fromY) {
|
||||||
|
drawLine(toX, toY, fromX, fromY, color)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
RenderSystem.lineWidth(MC.window.scaleFactor.toFloat())
|
||||||
|
val buf = this.vertexConsumers.getBuffer(RenderInWorldContext.RenderLayers.LINES)
|
||||||
|
buf.vertex(fromX.toFloat(), fromY.toFloat(), 0F).color(color.color)
|
||||||
|
.normal(toX - fromX.toFloat(), toY - fromY.toFloat(), 0F)
|
||||||
|
buf.vertex(toX.toFloat(), toY.toFloat(), 0F).color(color.color)
|
||||||
|
.normal(toX - fromX.toFloat(), toY - fromY.toFloat(), 0F)
|
||||||
|
this.draw()
|
||||||
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -177,6 +177,8 @@
|
|||||||
"firmament.config.slot-locking": "Slot Locking",
|
"firmament.config.slot-locking": "Slot Locking",
|
||||||
"firmament.config.slot-locking.lock": "Lock Slot",
|
"firmament.config.slot-locking.lock": "Lock Slot",
|
||||||
"firmament.config.slot-locking.lock-uuid": "Lock UUID (Lock Item)",
|
"firmament.config.slot-locking.lock-uuid": "Lock UUID (Lock Item)",
|
||||||
|
"firmament.config.slot-locking.bind": "Bind Slot",
|
||||||
|
"firmament.config.slot-locking.require-quick-move": "Require Shift-Click for Bind",
|
||||||
"firmament.config.fixes.auto-sprint": "Auto Sprint",
|
"firmament.config.fixes.auto-sprint": "Auto Sprint",
|
||||||
"firmament.config.fixes.auto-sprint-keybinding": "Auto Sprint KeyBinding",
|
"firmament.config.fixes.auto-sprint-keybinding": "Auto Sprint KeyBinding",
|
||||||
"firmament.config.fixes.auto-sprint-hud": "Sprint State Hud",
|
"firmament.config.fixes.auto-sprint-hud": "Sprint State Hud",
|
||||||
|
|||||||
Reference in New Issue
Block a user