Add interactive storage overlay
This commit is contained in:
2
.gitignore
vendored
2
.gitignore
vendored
@@ -28,4 +28,4 @@ gradle-app.setting
|
|||||||
# loom runClient
|
# loom runClient
|
||||||
/run/
|
/run/
|
||||||
*.xsd
|
*.xsd
|
||||||
|
*.png~
|
||||||
|
|||||||
77
src/main/java/moe/nea/firmament/init/ClientPlayerRiser.java
Normal file
77
src/main/java/moe/nea/firmament/init/ClientPlayerRiser.java
Normal file
@@ -0,0 +1,77 @@
|
|||||||
|
/*
|
||||||
|
* SPDX-FileCopyrightText: 2024 Linnea Gräf <nea@nea.moe>
|
||||||
|
*
|
||||||
|
* SPDX-License-Identifier: GPL-3.0-or-later
|
||||||
|
*/
|
||||||
|
|
||||||
|
package moe.nea.firmament.init;
|
||||||
|
|
||||||
|
import me.shedaniel.mm.api.ClassTinkerers;
|
||||||
|
import org.objectweb.asm.Opcodes;
|
||||||
|
import org.objectweb.asm.Type;
|
||||||
|
import org.objectweb.asm.tree.ClassNode;
|
||||||
|
import org.objectweb.asm.tree.InsnNode;
|
||||||
|
import org.objectweb.asm.tree.MethodInsnNode;
|
||||||
|
import org.objectweb.asm.tree.MethodNode;
|
||||||
|
import org.objectweb.asm.tree.VarInsnNode;
|
||||||
|
|
||||||
|
import java.lang.reflect.Modifier;
|
||||||
|
import java.util.Objects;
|
||||||
|
|
||||||
|
public class ClientPlayerRiser extends RiserUtils {
|
||||||
|
String PlayerEntity = remapper.mapClassName("intermediary", "net.minecraft.class_1657");
|
||||||
|
String World = remapper.mapClassName("intermediary", "net.minecraft.class_1937");
|
||||||
|
String GameProfile = "com.mojang.authlib.GameProfile";
|
||||||
|
String BlockPos = remapper.mapClassName("intermediary", "net.minecraft.class_2338");
|
||||||
|
String AbstractClientPlayerEntity = remapper.mapClassName("intermediary", "net.minecraft.class_742");
|
||||||
|
String GuiPlayer = "moe.nea.firmament.gui.entity.GuiPlayer";
|
||||||
|
// World world, BlockPos pos, float yaw, GameProfile gameProfile
|
||||||
|
Type constructorDescriptor = Type.getMethodType(Type.VOID_TYPE, getTypeForClassName(World), getTypeForClassName(BlockPos), Type.FLOAT_TYPE, getTypeForClassName(GameProfile));
|
||||||
|
|
||||||
|
|
||||||
|
private void mapClassNode(ClassNode classNode, Type superClass) {
|
||||||
|
for (MethodNode method : classNode.methods) {
|
||||||
|
if (Objects.equals(method.name, "<init>") && Type.getMethodType(method.desc).equals(constructorDescriptor)) {
|
||||||
|
modifyConstructor(method, superClass);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
var node = new MethodNode(Opcodes.ASM9, "<init>", constructorDescriptor.getDescriptor(), null, null);
|
||||||
|
classNode.methods.add(node);
|
||||||
|
modifyConstructor(node, superClass);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
private void modifyConstructor(MethodNode method, Type superClass) {
|
||||||
|
method.access = (method.access | Modifier.PUBLIC) & ~Modifier.PRIVATE & ~Modifier.PROTECTED;
|
||||||
|
if (method.instructions.size() != 0) return; // Some other mod has already made a constructor here
|
||||||
|
|
||||||
|
// World world, BlockPos pos, float yaw, GameProfile gameProfile
|
||||||
|
// ALOAD this
|
||||||
|
method.instructions.add(new VarInsnNode(Opcodes.ALOAD, 0));
|
||||||
|
|
||||||
|
// ALOAD World
|
||||||
|
method.instructions.add(new VarInsnNode(Opcodes.ALOAD, 1));
|
||||||
|
|
||||||
|
// ALOAD BlockPos
|
||||||
|
method.instructions.add(new VarInsnNode(Opcodes.ALOAD, 2));
|
||||||
|
|
||||||
|
// ALOAD yaw
|
||||||
|
method.instructions.add(new VarInsnNode(Opcodes.FLOAD, 3));
|
||||||
|
|
||||||
|
// ALOAD gameProfile
|
||||||
|
method.instructions.add(new VarInsnNode(Opcodes.ALOAD, 4));
|
||||||
|
|
||||||
|
// Call super
|
||||||
|
method.instructions.add(new MethodInsnNode(Opcodes.INVOKESPECIAL, superClass.getInternalName(), "<init>", constructorDescriptor.getDescriptor(), false));
|
||||||
|
|
||||||
|
// Return
|
||||||
|
method.instructions.add(new InsnNode(Opcodes.RETURN));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void addTinkerers() {
|
||||||
|
ClassTinkerers.addTransformation(AbstractClientPlayerEntity, it -> mapClassNode(it, getTypeForClassName(PlayerEntity)));
|
||||||
|
ClassTinkerers.addTransformation(GuiPlayer, it -> mapClassNode(it, getTypeForClassName(AbstractClientPlayerEntity)));
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -6,73 +6,10 @@
|
|||||||
|
|
||||||
package moe.nea.firmament.init;
|
package moe.nea.firmament.init;
|
||||||
|
|
||||||
import me.shedaniel.mm.api.ClassTinkerers;
|
|
||||||
import net.fabricmc.loader.api.FabricLoader;
|
|
||||||
import net.fabricmc.loader.api.MappingResolver;
|
|
||||||
import org.objectweb.asm.Opcodes;
|
|
||||||
import org.objectweb.asm.Type;
|
|
||||||
import org.objectweb.asm.tree.*;
|
|
||||||
|
|
||||||
import java.lang.reflect.Modifier;
|
|
||||||
import java.util.Objects;
|
|
||||||
|
|
||||||
public class EarlyRiser implements Runnable {
|
public class EarlyRiser implements Runnable {
|
||||||
MappingResolver remapper = FabricLoader.getInstance().getMappingResolver();
|
|
||||||
String PlayerEntity = remapper.mapClassName("intermediary", "net.minecraft.class_1657");
|
|
||||||
String World = remapper.mapClassName("intermediary", "net.minecraft.class_1937");
|
|
||||||
String GameProfile = "com.mojang.authlib.GameProfile";
|
|
||||||
String BlockPos = remapper.mapClassName("intermediary", "net.minecraft.class_2338");
|
|
||||||
String AbstractClientPlayerEntity = remapper.mapClassName("intermediary", "net.minecraft.class_742");
|
|
||||||
String GuiPlayer = "moe.nea.firmament.gui.entity.GuiPlayer";
|
|
||||||
// World world, BlockPos pos, float yaw, GameProfile gameProfile
|
|
||||||
Type constructorDescriptor = Type.getMethodType(Type.VOID_TYPE, getTypeForClassName(World), getTypeForClassName(BlockPos), Type.FLOAT_TYPE, getTypeForClassName(GameProfile));
|
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void run() {
|
public void run() {
|
||||||
ClassTinkerers.addTransformation(AbstractClientPlayerEntity, it -> mapClassNode(it, getTypeForClassName(PlayerEntity)));
|
new ClientPlayerRiser().addTinkerers();
|
||||||
ClassTinkerers.addTransformation(GuiPlayer, it -> mapClassNode(it, getTypeForClassName(AbstractClientPlayerEntity)));
|
new HandledScreenRiser().addTinkerers();
|
||||||
}
|
|
||||||
|
|
||||||
private void mapClassNode(ClassNode classNode, Type superClass) {
|
|
||||||
for (MethodNode method : classNode.methods) {
|
|
||||||
if (Objects.equals(method.name, "<init>") && Type.getMethodType(method.desc).equals(constructorDescriptor)) {
|
|
||||||
modifyConstructor(method, superClass);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
var node = new MethodNode(Opcodes.ASM9, "<init>", constructorDescriptor.getDescriptor(), null, null);
|
|
||||||
classNode.methods.add(node);
|
|
||||||
modifyConstructor(node, superClass);
|
|
||||||
}
|
|
||||||
|
|
||||||
private Type getTypeForClassName(String className) {
|
|
||||||
return Type.getObjectType(className.replace('.', '/'));
|
|
||||||
}
|
|
||||||
|
|
||||||
private void modifyConstructor(MethodNode method, Type superClass) {
|
|
||||||
method.access = (method.access | Modifier.PUBLIC) & ~Modifier.PRIVATE & ~Modifier.PROTECTED;
|
|
||||||
if (method.instructions.size() != 0) return; // Some other mod has already made a constructor here
|
|
||||||
|
|
||||||
// World world, BlockPos pos, float yaw, GameProfile gameProfile
|
|
||||||
// ALOAD this
|
|
||||||
method.instructions.add(new VarInsnNode(Opcodes.ALOAD, 0));
|
|
||||||
|
|
||||||
// ALOAD World
|
|
||||||
method.instructions.add(new VarInsnNode(Opcodes.ALOAD, 1));
|
|
||||||
|
|
||||||
// ALOAD BlockPos
|
|
||||||
method.instructions.add(new VarInsnNode(Opcodes.ALOAD, 2));
|
|
||||||
|
|
||||||
// ALOAD yaw
|
|
||||||
method.instructions.add(new VarInsnNode(Opcodes.FLOAD, 3));
|
|
||||||
|
|
||||||
// ALOAD gameProfile
|
|
||||||
method.instructions.add(new VarInsnNode(Opcodes.ALOAD, 4));
|
|
||||||
|
|
||||||
// Call super
|
|
||||||
method.instructions.add(new MethodInsnNode(Opcodes.INVOKESPECIAL, superClass.getInternalName(), "<init>", constructorDescriptor.getDescriptor(), false));
|
|
||||||
|
|
||||||
// Return
|
|
||||||
method.instructions.add(new InsnNode(Opcodes.RETURN));
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
86
src/main/java/moe/nea/firmament/init/HandledScreenRiser.java
Normal file
86
src/main/java/moe/nea/firmament/init/HandledScreenRiser.java
Normal file
@@ -0,0 +1,86 @@
|
|||||||
|
/*
|
||||||
|
* SPDX-FileCopyrightText: 2024 Linnea Gräf <nea@nea.moe>
|
||||||
|
*
|
||||||
|
* SPDX-License-Identifier: GPL-3.0-or-later
|
||||||
|
*/
|
||||||
|
|
||||||
|
package moe.nea.firmament.init;
|
||||||
|
|
||||||
|
import me.shedaniel.mm.api.ClassTinkerers;
|
||||||
|
import org.objectweb.asm.Opcodes;
|
||||||
|
import org.objectweb.asm.Type;
|
||||||
|
import org.objectweb.asm.tree.ClassNode;
|
||||||
|
import org.objectweb.asm.tree.InsnList;
|
||||||
|
import org.objectweb.asm.tree.InsnNode;
|
||||||
|
import org.objectweb.asm.tree.JumpInsnNode;
|
||||||
|
import org.objectweb.asm.tree.LabelNode;
|
||||||
|
import org.objectweb.asm.tree.LdcInsnNode;
|
||||||
|
import org.objectweb.asm.tree.MethodInsnNode;
|
||||||
|
import org.objectweb.asm.tree.MethodNode;
|
||||||
|
import org.objectweb.asm.tree.VarInsnNode;
|
||||||
|
|
||||||
|
import java.lang.reflect.Modifier;
|
||||||
|
|
||||||
|
public class HandledScreenRiser extends RiserUtils {
|
||||||
|
String Screen = remapper.mapClassName("intermediary", "net.minecraft.class_437");
|
||||||
|
String HandledScreen = remapper.mapClassName("intermediary", "net.minecraft.class_465");
|
||||||
|
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",
|
||||||
|
mouseScrolledDesc.getDescriptor());
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void addTinkerers() {
|
||||||
|
ClassTinkerers.addTransformation(HandledScreen, this::handle);
|
||||||
|
}
|
||||||
|
|
||||||
|
void handle(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);
|
||||||
|
}
|
||||||
|
|
||||||
|
var insns = new InsnList();
|
||||||
|
// 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));
|
||||||
|
// INVOKEVIRTUAL call custom handler
|
||||||
|
insns.add(new MethodInsnNode(Opcodes.INVOKEVIRTUAL,
|
||||||
|
getTypeForClassName(HandledScreen).getInternalName(),
|
||||||
|
"mouseScrolled_firmament",
|
||||||
|
mouseScrolledDesc.getDescriptor()));
|
||||||
|
// Create jump target (but not insert it yet)
|
||||||
|
var jumpIfFalse = new LabelNode();
|
||||||
|
// IFEQ (if returned boolean == 0), jump to jumpIfFalse
|
||||||
|
insns.add(new JumpInsnNode(Opcodes.IFEQ, jumpIfFalse));
|
||||||
|
// LDC 1 (as int, which is what booleans are at runtime)
|
||||||
|
insns.add(new LdcInsnNode(1));
|
||||||
|
// IRETURN return int on stack (booleans are int at runtime)
|
||||||
|
insns.add(new InsnNode(Opcodes.IRETURN));
|
||||||
|
insns.add(jumpIfFalse);
|
||||||
|
mouseScrolledNode.instructions.insert(insns);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
32
src/main/java/moe/nea/firmament/init/RiserUtils.java
Normal file
32
src/main/java/moe/nea/firmament/init/RiserUtils.java
Normal file
@@ -0,0 +1,32 @@
|
|||||||
|
/*
|
||||||
|
* SPDX-FileCopyrightText: 2024 Linnea Gräf <nea@nea.moe>
|
||||||
|
*
|
||||||
|
* SPDX-License-Identifier: GPL-3.0-or-later
|
||||||
|
*/
|
||||||
|
|
||||||
|
package moe.nea.firmament.init;
|
||||||
|
|
||||||
|
import net.fabricmc.loader.api.FabricLoader;
|
||||||
|
import net.fabricmc.loader.api.MappingResolver;
|
||||||
|
import org.objectweb.asm.Type;
|
||||||
|
import org.objectweb.asm.tree.ClassNode;
|
||||||
|
import org.objectweb.asm.tree.MethodNode;
|
||||||
|
|
||||||
|
public abstract class RiserUtils {
|
||||||
|
protected Type getTypeForClassName(String className) {
|
||||||
|
return Type.getObjectType(className.replace('.', '/'));
|
||||||
|
}
|
||||||
|
|
||||||
|
protected MappingResolver remapper = FabricLoader.getInstance().getMappingResolver();
|
||||||
|
|
||||||
|
public abstract void addTinkerers();
|
||||||
|
|
||||||
|
protected MethodNode findMethod(ClassNode classNode, String name, Type desc) {
|
||||||
|
for (MethodNode method : classNode.methods) {
|
||||||
|
if (method.name.equals(name) && desc.getDescriptor().equals(method.desc))
|
||||||
|
return method;
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
@@ -1,11 +1,14 @@
|
|||||||
/*
|
/*
|
||||||
* SPDX-FileCopyrightText: 2023 Linnea Gräf <nea@nea.moe>
|
* SPDX-FileCopyrightText: 2023 Linnea Gräf <nea@nea.moe>
|
||||||
|
* SPDX-FileCopyrightText: 2024 Linnea Gräf <nea@nea.moe>
|
||||||
*
|
*
|
||||||
* SPDX-License-Identifier: GPL-3.0-or-later
|
* SPDX-License-Identifier: GPL-3.0-or-later
|
||||||
*/
|
*/
|
||||||
|
|
||||||
package moe.nea.firmament.mixins;
|
package moe.nea.firmament.mixins;
|
||||||
|
|
||||||
|
import com.llamalad7.mixinextras.sugar.Local;
|
||||||
|
import com.llamalad7.mixinextras.sugar.ref.LocalRef;
|
||||||
import moe.nea.firmament.events.ScreenChangeEvent;
|
import moe.nea.firmament.events.ScreenChangeEvent;
|
||||||
import net.minecraft.client.MinecraftClient;
|
import net.minecraft.client.MinecraftClient;
|
||||||
import net.minecraft.client.gui.screen.Screen;
|
import net.minecraft.client.gui.screen.Screen;
|
||||||
@@ -23,10 +26,12 @@ public abstract class ScreenChangeEventPatch {
|
|||||||
public Screen currentScreen;
|
public Screen currentScreen;
|
||||||
|
|
||||||
@Inject(method = "setScreen", at = @At("HEAD"), cancellable = true)
|
@Inject(method = "setScreen", at = @At("HEAD"), cancellable = true)
|
||||||
public void onScreenChange(Screen screen, CallbackInfo ci) {
|
public void onScreenChange(Screen screen, CallbackInfo ci, @Local(argsOnly = true) LocalRef<Screen> screenLocalRef) {
|
||||||
var event = new ScreenChangeEvent(currentScreen, screen);
|
var event = new ScreenChangeEvent(currentScreen, screen);
|
||||||
if (ScreenChangeEvent.Companion.publish(event).getCancelled()) {
|
if (ScreenChangeEvent.Companion.publish(event).getCancelled()) {
|
||||||
ci.cancel();
|
ci.cancel();
|
||||||
|
} else if (event.getOverrideScreen() != null) {
|
||||||
|
screenLocalRef.set(event.getOverrideScreen());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -0,0 +1,48 @@
|
|||||||
|
/*
|
||||||
|
* SPDX-FileCopyrightText: 2024 Linnea Gräf <nea@nea.moe>
|
||||||
|
*
|
||||||
|
* SPDX-License-Identifier: GPL-3.0-or-later
|
||||||
|
*/
|
||||||
|
|
||||||
|
package moe.nea.firmament.mixins.customgui;
|
||||||
|
|
||||||
|
import moe.nea.firmament.util.customgui.CoordRememberingSlot;
|
||||||
|
import net.minecraft.screen.slot.Slot;
|
||||||
|
import org.spongepowered.asm.mixin.Mixin;
|
||||||
|
import org.spongepowered.asm.mixin.Shadow;
|
||||||
|
import org.spongepowered.asm.mixin.Unique;
|
||||||
|
|
||||||
|
@Mixin(Slot.class)
|
||||||
|
public class OriginalSlotCoords implements CoordRememberingSlot {
|
||||||
|
|
||||||
|
@Shadow
|
||||||
|
public int x;
|
||||||
|
@Shadow
|
||||||
|
public int y;
|
||||||
|
@Unique
|
||||||
|
public int originalX;
|
||||||
|
@Unique
|
||||||
|
public int originalY;
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void rememberCoords_firmament() {
|
||||||
|
this.originalX = this.x;
|
||||||
|
this.originalY = this.y;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void restoreCoords_firmament() {
|
||||||
|
this.x = this.originalX;
|
||||||
|
this.y = this.originalY;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public int getOriginalX_firmament() {
|
||||||
|
return originalX;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public int getOriginalY_firmament() {
|
||||||
|
return originalY;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,175 @@
|
|||||||
|
/*
|
||||||
|
* SPDX-FileCopyrightText: 2024 Linnea Gräf <nea@nea.moe>
|
||||||
|
*
|
||||||
|
* SPDX-License-Identifier: GPL-3.0-or-later
|
||||||
|
*/
|
||||||
|
|
||||||
|
package moe.nea.firmament.mixins.customgui;
|
||||||
|
|
||||||
|
import com.llamalad7.mixinextras.injector.v2.WrapWithCondition;
|
||||||
|
import com.llamalad7.mixinextras.injector.wrapoperation.Operation;
|
||||||
|
import com.llamalad7.mixinextras.injector.wrapoperation.WrapOperation;
|
||||||
|
import com.llamalad7.mixinextras.sugar.Local;
|
||||||
|
import moe.nea.firmament.util.customgui.CoordRememberingSlot;
|
||||||
|
import moe.nea.firmament.util.customgui.CustomGui;
|
||||||
|
import moe.nea.firmament.util.customgui.HasCustomGui;
|
||||||
|
import net.minecraft.client.gui.DrawContext;
|
||||||
|
import net.minecraft.client.gui.screen.Screen;
|
||||||
|
import net.minecraft.client.gui.screen.ingame.HandledScreen;
|
||||||
|
import net.minecraft.screen.ScreenHandler;
|
||||||
|
import net.minecraft.screen.slot.Slot;
|
||||||
|
import net.minecraft.text.Text;
|
||||||
|
import net.minecraft.util.collection.DefaultedList;
|
||||||
|
import org.jetbrains.annotations.Nullable;
|
||||||
|
import org.spongepowered.asm.mixin.Final;
|
||||||
|
import org.spongepowered.asm.mixin.Mixin;
|
||||||
|
import org.spongepowered.asm.mixin.Shadow;
|
||||||
|
import org.spongepowered.asm.mixin.Unique;
|
||||||
|
import org.spongepowered.asm.mixin.injection.At;
|
||||||
|
import org.spongepowered.asm.mixin.injection.Inject;
|
||||||
|
import org.spongepowered.asm.mixin.injection.ModifyVariable;
|
||||||
|
import org.spongepowered.asm.mixin.injection.callback.CallbackInfo;
|
||||||
|
import org.spongepowered.asm.mixin.injection.callback.CallbackInfoReturnable;
|
||||||
|
|
||||||
|
@Mixin(HandledScreen.class)
|
||||||
|
public class PatchHandledScreen<T extends ScreenHandler> extends Screen implements HasCustomGui {
|
||||||
|
@Shadow
|
||||||
|
@Final
|
||||||
|
protected T handler;
|
||||||
|
@Shadow
|
||||||
|
protected int x;
|
||||||
|
@Shadow
|
||||||
|
protected int y;
|
||||||
|
@Unique
|
||||||
|
public CustomGui override;
|
||||||
|
@Unique
|
||||||
|
public boolean hasRememberedSlots = false;
|
||||||
|
|
||||||
|
protected PatchHandledScreen(Text title) {
|
||||||
|
super(title);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Nullable
|
||||||
|
@Override
|
||||||
|
public CustomGui getCustomGui_Firmament() {
|
||||||
|
return override;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void setCustomGui_Firmament(@Nullable CustomGui gui) {
|
||||||
|
this.override = gui;
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean mouseScrolled_firmament(double mouseX, double mouseY, double horizontalAmount, double verticalAmount) {
|
||||||
|
return override != null && override.mouseScrolled(mouseX, mouseY, horizontalAmount, verticalAmount);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Inject(method = "init", at = @At("TAIL"))
|
||||||
|
private void onInit(CallbackInfo ci) {
|
||||||
|
if (override != null) {
|
||||||
|
override.onInit();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Inject(method = "drawForeground", at = @At("HEAD"), cancellable = true)
|
||||||
|
private void onDrawForeground(DrawContext context, int mouseX, int mouseY, CallbackInfo ci) {
|
||||||
|
if (override != null && !override.shouldDrawForeground())
|
||||||
|
ci.cancel();
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@Unique
|
||||||
|
private Slot didBeforeSlotRender;
|
||||||
|
|
||||||
|
@WrapOperation(
|
||||||
|
method = "render",
|
||||||
|
at = @At(
|
||||||
|
value = "INVOKE",
|
||||||
|
target = "Lnet/minecraft/util/collection/DefaultedList;get(I)Ljava/lang/Object;"))
|
||||||
|
private Object beforeSlotRender(DefaultedList instance, int index, Operation<Object> original, @Local(argsOnly = true) DrawContext context) {
|
||||||
|
var slot = (Slot) original.call(instance, index);
|
||||||
|
if (override != null) {
|
||||||
|
didBeforeSlotRender = slot;
|
||||||
|
override.beforeSlotRender(context, slot);
|
||||||
|
}
|
||||||
|
return slot;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Inject(method = "render",
|
||||||
|
at = @At(value = "INVOKE", target = "Lnet/minecraft/util/collection/DefaultedList;size()I"))
|
||||||
|
private void afterSlotRender(DrawContext context, int mouseX, int mouseY, float delta, CallbackInfo ci) {
|
||||||
|
if (override != null && didBeforeSlotRender != null) {
|
||||||
|
override.afterSlotRender(context, didBeforeSlotRender);
|
||||||
|
didBeforeSlotRender = null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Inject(method = "isClickOutsideBounds", at = @At("HEAD"), cancellable = true)
|
||||||
|
public void onIsClickOutsideBounds(double mouseX, double mouseY, int left, int top, int button, CallbackInfoReturnable<Boolean> cir) {
|
||||||
|
if (override != null) {
|
||||||
|
cir.setReturnValue(override.isClickOutsideBounds(mouseX, mouseY));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Inject(method = "isPointWithinBounds", at = @At("HEAD"), cancellable = true)
|
||||||
|
public void onIsPointWithinBounds(int x, int y, int width, int height, double pointX, double pointY, CallbackInfoReturnable<Boolean> cir) {
|
||||||
|
if (override != null) {
|
||||||
|
cir.setReturnValue(override.isPointWithinBounds(x + this.x, y + this.y, width, height, pointX, pointY));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Inject(method = "isPointOverSlot", at = @At("HEAD"), cancellable = true)
|
||||||
|
public void onIsPointOverSlot(Slot slot, double pointX, double pointY, CallbackInfoReturnable<Boolean> cir) {
|
||||||
|
if (override != null) {
|
||||||
|
cir.setReturnValue(override.isPointOverSlot(slot, this.x, this.y, pointX, pointY));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Inject(method = "render", at = @At("HEAD"))
|
||||||
|
public void moveSlots(DrawContext context, int mouseX, int mouseY, float delta, CallbackInfo ci) {
|
||||||
|
if (override != null) {
|
||||||
|
for (Slot slot : handler.slots) {
|
||||||
|
if (!hasRememberedSlots) {
|
||||||
|
((CoordRememberingSlot) slot).rememberCoords_firmament();
|
||||||
|
}
|
||||||
|
override.moveSlot(slot);
|
||||||
|
}
|
||||||
|
hasRememberedSlots = true;
|
||||||
|
} else {
|
||||||
|
if (hasRememberedSlots) {
|
||||||
|
for (Slot slot : handler.slots) {
|
||||||
|
((CoordRememberingSlot) slot).restoreCoords_firmament();
|
||||||
|
}
|
||||||
|
hasRememberedSlots = false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Inject(at = @At("HEAD"), method = "close", cancellable = true)
|
||||||
|
private void onVoluntaryExit(CallbackInfo ci) {
|
||||||
|
if (override != null) {
|
||||||
|
if (!override.onVoluntaryExit())
|
||||||
|
ci.cancel();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@WrapWithCondition(method = "renderBackground", at = @At(value = "INVOKE", target = "Lnet/minecraft/client/gui/screen/ingame/HandledScreen;drawBackground(Lnet/minecraft/client/gui/DrawContext;FII)V"))
|
||||||
|
public boolean preventDrawingBackground(HandledScreen instance, DrawContext drawContext, float delta, int mouseX, int mouseY) {
|
||||||
|
if (override != null) {
|
||||||
|
override.render(drawContext, delta, mouseX, mouseY);
|
||||||
|
}
|
||||||
|
return override == null;
|
||||||
|
}
|
||||||
|
|
||||||
|
@WrapOperation(
|
||||||
|
method = "mouseClicked",
|
||||||
|
at = @At(value = "INVOKE", target = "Lnet/minecraft/client/gui/screen/Screen;mouseClicked(DDI)Z"))
|
||||||
|
public boolean overrideMouseClicks(HandledScreen instance, double mouseX, double mouseY, int button,
|
||||||
|
Operation<Boolean> original) {
|
||||||
|
if (override != null) {
|
||||||
|
if (override.mouseClick(mouseX, mouseY, button))
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
return original.call(instance, mouseX, mouseY, button);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -15,7 +15,7 @@ import net.minecraft.text.Text
|
|||||||
import moe.nea.firmament.apis.UrsaManager
|
import moe.nea.firmament.apis.UrsaManager
|
||||||
import moe.nea.firmament.events.CommandEvent
|
import moe.nea.firmament.events.CommandEvent
|
||||||
import moe.nea.firmament.features.inventory.buttons.InventoryButtons
|
import moe.nea.firmament.features.inventory.buttons.InventoryButtons
|
||||||
import moe.nea.firmament.features.inventory.storageoverlay.StorageOverlayScreen
|
import moe.nea.firmament.features.inventory.storageoverlay.StorageOverviewScreen
|
||||||
import moe.nea.firmament.features.world.FairySouls
|
import moe.nea.firmament.features.world.FairySouls
|
||||||
import moe.nea.firmament.gui.config.AllConfigsGui
|
import moe.nea.firmament.gui.config.AllConfigsGui
|
||||||
import moe.nea.firmament.gui.config.BooleanHandler
|
import moe.nea.firmament.gui.config.BooleanHandler
|
||||||
@@ -107,7 +107,7 @@ fun firmamentCommand() = literal("firmament") {
|
|||||||
}
|
}
|
||||||
thenLiteral("storage") {
|
thenLiteral("storage") {
|
||||||
thenExecute {
|
thenExecute {
|
||||||
ScreenUtil.setScreenLater(StorageOverlayScreen())
|
ScreenUtil.setScreenLater(StorageOverviewScreen())
|
||||||
MC.player?.networkHandler?.sendChatCommand("storage")
|
MC.player?.networkHandler?.sendChatCommand("storage")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,5 +1,6 @@
|
|||||||
/*
|
/*
|
||||||
* SPDX-FileCopyrightText: 2023 Linnea Gräf <nea@nea.moe>
|
* SPDX-FileCopyrightText: 2023 Linnea Gräf <nea@nea.moe>
|
||||||
|
* SPDX-FileCopyrightText: 2024 Linnea Gräf <nea@nea.moe>
|
||||||
*
|
*
|
||||||
* SPDX-License-Identifier: GPL-3.0-or-later
|
* SPDX-License-Identifier: GPL-3.0-or-later
|
||||||
*/
|
*/
|
||||||
@@ -9,5 +10,6 @@ package moe.nea.firmament.events
|
|||||||
import net.minecraft.client.gui.screen.Screen
|
import net.minecraft.client.gui.screen.Screen
|
||||||
|
|
||||||
data class ScreenChangeEvent(val old: Screen?, val new: Screen?) : FirmamentEvent.Cancellable() {
|
data class ScreenChangeEvent(val old: Screen?, val new: Screen?) : FirmamentEvent.Cancellable() {
|
||||||
|
var overrideScreen: Screen? = null
|
||||||
companion object : FirmamentEventBus<ScreenChangeEvent>()
|
companion object : FirmamentEventBus<ScreenChangeEvent>()
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -15,7 +15,7 @@ interface SubscriptionOwner {
|
|||||||
}
|
}
|
||||||
|
|
||||||
data class Subscription<T : FirmamentEvent>(
|
data class Subscription<T : FirmamentEvent>(
|
||||||
val owner: SubscriptionOwner,
|
val owner: Any,
|
||||||
val invoke: (T) -> Unit,
|
val invoke: (T) -> Unit,
|
||||||
val eventBus: FirmamentEventBus<T>,
|
val eventBus: FirmamentEventBus<T>,
|
||||||
)
|
)
|
||||||
|
|||||||
@@ -98,11 +98,7 @@ object FeatureManager : DataHolder<FeatureManager.Config>(serializer(), "feature
|
|||||||
}
|
}
|
||||||
|
|
||||||
private fun <T : FirmamentEvent> subscribeSingleEvent(it: Subscription<T>) {
|
private fun <T : FirmamentEvent> subscribeSingleEvent(it: Subscription<T>) {
|
||||||
if (it.owner.delegateFeature in features.values) { // TODO: better check here, somehow. probably implement some interface method
|
it.eventBus.subscribe(false, it.invoke)
|
||||||
it.eventBus.subscribe(false, it.invoke) // TODO: pass through receivesCancelled from the annotation
|
|
||||||
} else {
|
|
||||||
Firmament.logger.error("Ignoring event listener for ${it.eventBus} in ${it.owner}")
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
fun loadFeature(feature: FirmamentFeature) {
|
fun loadFeature(feature: FirmamentFeature) {
|
||||||
|
|||||||
@@ -1,5 +1,6 @@
|
|||||||
/*
|
/*
|
||||||
* SPDX-FileCopyrightText: 2023 Linnea Gräf <nea@nea.moe>
|
* SPDX-FileCopyrightText: 2023 Linnea Gräf <nea@nea.moe>
|
||||||
|
* SPDX-FileCopyrightText: 2024 Linnea Gräf <nea@nea.moe>
|
||||||
*
|
*
|
||||||
* SPDX-License-Identifier: GPL-3.0-or-later
|
* SPDX-License-Identifier: GPL-3.0-or-later
|
||||||
*/
|
*/
|
||||||
@@ -21,11 +22,6 @@ sealed interface StorageBackingHandle {
|
|||||||
val handler: GenericContainerScreenHandler
|
val handler: GenericContainerScreenHandler
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* No open "server side" screen.
|
|
||||||
*/
|
|
||||||
object None : StorageBackingHandle
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* The main storage overview is open. Clicking on a slot will open that page. This page is accessible via `/storage`
|
* The main storage overview is open. Clicking on a slot will open that page. This page is accessible via `/storage`
|
||||||
*/
|
*/
|
||||||
@@ -48,7 +44,7 @@ sealed interface StorageBackingHandle {
|
|||||||
* selection screen.
|
* selection screen.
|
||||||
*/
|
*/
|
||||||
fun fromScreen(screen: Screen?): StorageBackingHandle? {
|
fun fromScreen(screen: Screen?): StorageBackingHandle? {
|
||||||
if (screen == null) return None
|
if (screen == null) return null
|
||||||
if (screen !is GenericContainerScreen) return null
|
if (screen !is GenericContainerScreen) return null
|
||||||
val title = screen.title.unformattedString
|
val title = screen.title.unformattedString
|
||||||
if (title == "Storage") return Overview(screen.screenHandler)
|
if (title == "Storage") return Overview(screen.screenHandler)
|
||||||
|
|||||||
@@ -1,5 +1,6 @@
|
|||||||
/*
|
/*
|
||||||
* SPDX-FileCopyrightText: 2023 Linnea Gräf <nea@nea.moe>
|
* SPDX-FileCopyrightText: 2023 Linnea Gräf <nea@nea.moe>
|
||||||
|
* SPDX-FileCopyrightText: 2024 Linnea Gräf <nea@nea.moe>
|
||||||
*
|
*
|
||||||
* SPDX-License-Identifier: GPL-3.0-or-later
|
* SPDX-License-Identifier: GPL-3.0-or-later
|
||||||
*/
|
*/
|
||||||
@@ -9,12 +10,14 @@ package moe.nea.firmament.features.inventory.storageoverlay
|
|||||||
import java.util.SortedMap
|
import java.util.SortedMap
|
||||||
import kotlinx.serialization.serializer
|
import kotlinx.serialization.serializer
|
||||||
import net.minecraft.client.gui.screen.Screen
|
import net.minecraft.client.gui.screen.Screen
|
||||||
|
import net.minecraft.client.gui.screen.ingame.GenericContainerScreen
|
||||||
|
import net.minecraft.client.gui.screen.ingame.HandledScreen
|
||||||
import moe.nea.firmament.annotations.Subscribe
|
import moe.nea.firmament.annotations.Subscribe
|
||||||
import moe.nea.firmament.events.ScreenChangeEvent
|
import moe.nea.firmament.events.ScreenChangeEvent
|
||||||
import moe.nea.firmament.events.TickEvent
|
import moe.nea.firmament.events.TickEvent
|
||||||
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
|
||||||
import moe.nea.firmament.util.ScreenUtil.setScreenLater
|
import moe.nea.firmament.util.customgui.customGui
|
||||||
import moe.nea.firmament.util.data.ProfileSpecificDataHolder
|
import moe.nea.firmament.util.data.ProfileSpecificDataHolder
|
||||||
|
|
||||||
object StorageOverlay : FirmamentFeature {
|
object StorageOverlay : FirmamentFeature {
|
||||||
@@ -33,49 +36,41 @@ object StorageOverlay : FirmamentFeature {
|
|||||||
val margin by integer("margin", 1, 60) { 20 }
|
val margin by integer("margin", 1, 60) { 20 }
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fun adjustScrollSpeed(amount: Double): Double {
|
||||||
|
return amount * TConfig.scrollSpeed * (if (TConfig.inverseScroll) 1 else -1)
|
||||||
|
}
|
||||||
|
|
||||||
override val config: TConfig
|
override val config: TConfig
|
||||||
get() = TConfig
|
get() = TConfig
|
||||||
|
|
||||||
var lastStorageOverlay: Screen? = null
|
var lastStorageOverlay: Screen? = null
|
||||||
var shouldReturnToStorageOverlayFrom: Screen? = null
|
var currentHandler: StorageBackingHandle? = null
|
||||||
var shouldReturnToStorageOverlay: Screen? = null
|
|
||||||
var currentHandler: StorageBackingHandle? = StorageBackingHandle.None
|
|
||||||
|
|
||||||
@Subscribe
|
@Subscribe
|
||||||
fun onTick(event: TickEvent) {
|
fun onTick(event: TickEvent) {
|
||||||
rememberContent(currentHandler ?: return)
|
rememberContent(currentHandler ?: return)
|
||||||
}
|
}
|
||||||
|
|
||||||
@Subscribe
|
|
||||||
fun onScreenChangeLegacy(event: ScreenChangeEvent) {
|
|
||||||
currentHandler = StorageBackingHandle.fromScreen(event.new)
|
|
||||||
if (event.old is StorageOverlayScreen && !event.old.isClosing) {
|
|
||||||
event.old.setHandler(currentHandler)
|
|
||||||
if (currentHandler != null)
|
|
||||||
// TODO: Consider instead only replacing rendering? might make a lot of stack handling easier
|
|
||||||
event.cancel()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@Subscribe
|
@Subscribe
|
||||||
fun onScreenChange(it: ScreenChangeEvent) {
|
fun onScreenChange(it: ScreenChangeEvent) {
|
||||||
if (lastStorageOverlay != null && it.new != null) {
|
val storageOverlayScreen = it.old as? StorageOverlayScreen
|
||||||
shouldReturnToStorageOverlay = lastStorageOverlay
|
?: ((it.old as? HandledScreen<*>)?.customGui as? StorageOverlayCustom)?.overview
|
||||||
shouldReturnToStorageOverlayFrom = it.new
|
if (it.new == null && storageOverlayScreen != null && !storageOverlayScreen.isExiting) {
|
||||||
lastStorageOverlay = null
|
it.overrideScreen = storageOverlayScreen
|
||||||
} else if (it.old === shouldReturnToStorageOverlayFrom) {
|
return
|
||||||
if (shouldReturnToStorageOverlay != null && it.new == null)
|
|
||||||
setScreenLater(shouldReturnToStorageOverlay)
|
|
||||||
shouldReturnToStorageOverlay = null
|
|
||||||
shouldReturnToStorageOverlayFrom = null
|
|
||||||
}
|
}
|
||||||
|
val screen = it.new as? GenericContainerScreen ?: return
|
||||||
|
currentHandler = StorageBackingHandle.fromScreen(screen)
|
||||||
|
screen.customGui = StorageOverlayCustom(
|
||||||
|
currentHandler as? StorageBackingHandle.Page ?: return,
|
||||||
|
screen,
|
||||||
|
storageOverlayScreen ?: return)
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun rememberContent(handler: StorageBackingHandle) {
|
fun rememberContent(handler: StorageBackingHandle) {
|
||||||
// TODO: Make all of these functions work on deltas / updates instead of the entire contents
|
// TODO: Make all of these functions work on deltas / updates instead of the entire contents
|
||||||
val data = Data.data?.storageInventories ?: return
|
val data = Data.data?.storageInventories ?: return
|
||||||
when (handler) {
|
when (handler) {
|
||||||
StorageBackingHandle.None -> {}
|
|
||||||
is StorageBackingHandle.Overview -> rememberStorageOverview(handler, data)
|
is StorageBackingHandle.Overview -> rememberStorageOverview(handler, data)
|
||||||
is StorageBackingHandle.Page -> rememberPage(handler, data)
|
is StorageBackingHandle.Page -> rememberPage(handler, data)
|
||||||
}
|
}
|
||||||
@@ -90,7 +85,7 @@ object StorageOverlay : FirmamentFeature {
|
|||||||
// Ignore unloaded item stacks
|
// Ignore unloaded item stacks
|
||||||
if (stack.isEmpty) continue
|
if (stack.isEmpty) continue
|
||||||
val slot = StoragePageSlot.fromOverviewSlotIndex(index) ?: continue
|
val slot = StoragePageSlot.fromOverviewSlotIndex(index) ?: continue
|
||||||
val isEmpty = stack.item in StorageOverlayScreen.emptyStorageSlotItems
|
val isEmpty = stack.item in StorageOverviewScreen.emptyStorageSlotItems
|
||||||
if (slot in data) {
|
if (slot in data) {
|
||||||
if (isEmpty)
|
if (isEmpty)
|
||||||
data.remove(slot)
|
data.remove(slot)
|
||||||
|
|||||||
@@ -0,0 +1,97 @@
|
|||||||
|
/*
|
||||||
|
* SPDX-FileCopyrightText: 2024 Linnea Gräf <nea@nea.moe>
|
||||||
|
*
|
||||||
|
* SPDX-License-Identifier: GPL-3.0-or-later
|
||||||
|
*/
|
||||||
|
|
||||||
|
package moe.nea.firmament.features.inventory.storageoverlay
|
||||||
|
|
||||||
|
import me.shedaniel.math.Point
|
||||||
|
import me.shedaniel.math.Rectangle
|
||||||
|
import net.minecraft.client.MinecraftClient
|
||||||
|
import net.minecraft.client.gui.DrawContext
|
||||||
|
import net.minecraft.client.gui.screen.ingame.GenericContainerScreen
|
||||||
|
import net.minecraft.entity.player.PlayerInventory
|
||||||
|
import net.minecraft.screen.slot.Slot
|
||||||
|
import moe.nea.firmament.mixins.accessor.AccessorHandledScreen
|
||||||
|
import moe.nea.firmament.util.customgui.CustomGui
|
||||||
|
|
||||||
|
class StorageOverlayCustom(
|
||||||
|
val handler: StorageBackingHandle.Page,
|
||||||
|
val screen: GenericContainerScreen,
|
||||||
|
val overview: StorageOverlayScreen,
|
||||||
|
) : CustomGui() {
|
||||||
|
override fun onVoluntaryExit(): Boolean {
|
||||||
|
overview.isExiting = true
|
||||||
|
return super.onVoluntaryExit()
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun getBounds(): List<Rectangle> {
|
||||||
|
return overview.getBounds()
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun afterSlotRender(context: DrawContext, slot: Slot) {
|
||||||
|
if (slot.inventory !is PlayerInventory)
|
||||||
|
context.disableScissor()
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun beforeSlotRender(context: DrawContext, slot: Slot) {
|
||||||
|
if (slot.inventory !is PlayerInventory)
|
||||||
|
overview.createScissors(context)
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun onInit() {
|
||||||
|
overview.init(MinecraftClient.getInstance(), screen.width, screen.height)
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun isPointOverSlot(slot: Slot, xOffset: Int, yOffset: Int, pointX: Double, pointY: Double): Boolean {
|
||||||
|
if (!super.isPointOverSlot(slot, xOffset, yOffset, pointX, pointY))
|
||||||
|
return false
|
||||||
|
if (slot.inventory !is PlayerInventory) {
|
||||||
|
if (!overview.getScrollPanelInner().contains(pointX, pointY))
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun shouldDrawForeground(): Boolean {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun mouseClick(mouseX: Double, mouseY: Double, button: Int): Boolean {
|
||||||
|
return overview.mouseClicked(mouseX, mouseY, button, handler.storagePageSlot)
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun render(drawContext: DrawContext, delta: Float, mouseX: Int, mouseY: Int) {
|
||||||
|
overview.drawBackgrounds(drawContext)
|
||||||
|
overview.drawPages(drawContext,
|
||||||
|
mouseX,
|
||||||
|
mouseY,
|
||||||
|
delta,
|
||||||
|
handler.storagePageSlot,
|
||||||
|
screen.screenHandler.slots.take(screen.screenHandler.rows * 9).drop(9),
|
||||||
|
Point((screen as AccessorHandledScreen).x_Firmament, screen.y_Firmament))
|
||||||
|
overview.drawScrollBar(drawContext)
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun moveSlot(slot: Slot) {
|
||||||
|
val index = slot.index
|
||||||
|
if (index in 0..<36) {
|
||||||
|
val (x, y) = overview.getPlayerInventorySlotPosition(index)
|
||||||
|
slot.x = x - (screen as AccessorHandledScreen).x_Firmament
|
||||||
|
slot.y = y - screen.y_Firmament
|
||||||
|
} else {
|
||||||
|
slot.x = -100000
|
||||||
|
slot.y = -100000
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun mouseScrolled(
|
||||||
|
mouseX: Double,
|
||||||
|
mouseY: Double,
|
||||||
|
horizontalAmount: Double,
|
||||||
|
verticalAmount: Double
|
||||||
|
): Boolean {
|
||||||
|
return overview.mouseScrolled(mouseX, mouseY, horizontalAmount, verticalAmount)
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -1,106 +1,75 @@
|
|||||||
/*
|
/*
|
||||||
* SPDX-FileCopyrightText: 2023 Linnea Gräf <nea@nea.moe>
|
* SPDX-FileCopyrightText: 2024 Linnea Gräf <nea@nea.moe>
|
||||||
*
|
*
|
||||||
* SPDX-License-Identifier: GPL-3.0-or-later
|
* SPDX-License-Identifier: GPL-3.0-or-later
|
||||||
*/
|
*/
|
||||||
|
|
||||||
package moe.nea.firmament.features.inventory.storageoverlay
|
package moe.nea.firmament.features.inventory.storageoverlay
|
||||||
|
|
||||||
import moe.nea.firmament.util.MC
|
import me.shedaniel.math.Point
|
||||||
import moe.nea.firmament.util.assertNotNullOr
|
import me.shedaniel.math.Rectangle
|
||||||
import moe.nea.firmament.util.toShedaniel
|
|
||||||
import net.minecraft.block.Blocks
|
|
||||||
import net.minecraft.client.gui.DrawContext
|
import net.minecraft.client.gui.DrawContext
|
||||||
import net.minecraft.client.gui.screen.Screen
|
import net.minecraft.client.gui.screen.Screen
|
||||||
import net.minecraft.item.Item
|
import net.minecraft.screen.slot.Slot
|
||||||
import net.minecraft.item.Items
|
|
||||||
import net.minecraft.network.packet.c2s.play.CloseHandledScreenC2SPacket
|
|
||||||
import net.minecraft.text.Text
|
import net.minecraft.text.Text
|
||||||
import net.minecraft.util.DyeColor
|
import net.minecraft.util.Identifier
|
||||||
import kotlin.math.max
|
import moe.nea.firmament.annotations.Subscribe
|
||||||
|
import moe.nea.firmament.events.CommandEvent
|
||||||
|
import moe.nea.firmament.util.MC
|
||||||
|
import moe.nea.firmament.util.ScreenUtil
|
||||||
|
import moe.nea.firmament.util.assertTrueOr
|
||||||
|
|
||||||
|
class StorageOverlayScreen : Screen(Text.literal("")) {
|
||||||
|
|
||||||
class StorageOverlayScreen() : Screen(Text.empty()) {
|
|
||||||
companion object {
|
companion object {
|
||||||
val emptyStorageSlotItems = listOf<Item>(
|
val PLAYER_WIDTH = 184
|
||||||
Blocks.RED_STAINED_GLASS_PANE.asItem(),
|
val PLAYER_HEIGHT = 91
|
||||||
Blocks.BROWN_STAINED_GLASS_PANE.asItem(),
|
val PLAYER_Y_INSET = 3
|
||||||
Items.GRAY_DYE
|
val SLOT_SIZE = 18
|
||||||
)
|
val PADDING = 10
|
||||||
val pageWidth get() = 19 * 9
|
val PAGE_WIDTH = SLOT_SIZE * 9
|
||||||
|
val HOTBAR_X = 12
|
||||||
|
val HOTBAR_Y = 67
|
||||||
|
val MAIN_INVENTORY_Y = 9
|
||||||
|
val SCROLL_BAR_WIDTH = 8
|
||||||
|
val SCROLL_BAR_HEIGHT = 16
|
||||||
|
|
||||||
|
@Subscribe
|
||||||
|
fun onCommand(event: CommandEvent.SubCommand) {
|
||||||
|
event.subcommand("teststorage") {
|
||||||
|
executes {
|
||||||
|
ScreenUtil.setScreenLater(StorageOverlayScreen())
|
||||||
|
MC.sendCommand("ec")
|
||||||
|
0
|
||||||
}
|
}
|
||||||
|
|
||||||
private var handler: StorageBackingHandle = StorageBackingHandle.None
|
|
||||||
val content = StorageOverlay.Data.data ?: StorageData()
|
|
||||||
var isClosing = false
|
|
||||||
|
|
||||||
private fun discardOldHandle() {
|
|
||||||
val player = assertNotNullOr(MC.player) { return }
|
|
||||||
val handle = this.handler
|
|
||||||
if (handle is StorageBackingHandle.HasBackingScreen) {
|
|
||||||
player.networkHandler.sendPacket(CloseHandledScreenC2SPacket(handle.handler.syncId))
|
|
||||||
if (player.currentScreenHandler === handle.handler) {
|
|
||||||
player.currentScreenHandler = player.playerScreenHandler
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fun setHandler(handler: StorageBackingHandle?) {
|
var isExiting: Boolean = false
|
||||||
discardOldHandle()
|
var scroll: Float = 0F
|
||||||
if (handler != null)
|
var pageWidthCount = StorageOverlay.TConfig.rows
|
||||||
this.handler = handler
|
|
||||||
|
inner class Measurements {
|
||||||
|
val innerScrollPanelWidth = PAGE_WIDTH * pageWidthCount + (pageWidthCount - 1) * PADDING
|
||||||
|
val overviewWidth = innerScrollPanelWidth + 3 * PADDING + SCROLL_BAR_WIDTH
|
||||||
|
val x = width / 2 - overviewWidth / 2
|
||||||
|
val overviewHeight = minOf(3 * 18 * 6, height - PLAYER_HEIGHT - minOf(80, height / 10))
|
||||||
|
val innerScrollPanelHeight = overviewHeight - PADDING * 2
|
||||||
|
val y = height / 2 - (overviewHeight + PLAYER_HEIGHT) / 2
|
||||||
|
val playerX = width / 2 - PLAYER_WIDTH / 2
|
||||||
|
val playerY = y + overviewHeight - PLAYER_Y_INSET
|
||||||
}
|
}
|
||||||
|
|
||||||
var scroll = 0
|
var measurements = Measurements()
|
||||||
var lastRenderedHeight = 0
|
|
||||||
|
|
||||||
override fun render(context: DrawContext, mouseX: Int, mouseY: Int, delta: Float) {
|
var lastRenderedInnerHeight = 0
|
||||||
super.render(context, mouseX, mouseY, delta)
|
override fun init() {
|
||||||
context.fill(0, 0, width, height, 0x90000000.toInt())
|
super.init()
|
||||||
layoutedForEach { (key, value), offsetX, offsetY ->
|
pageWidthCount = StorageOverlay.TConfig.rows
|
||||||
context.matrices.push()
|
.coerceAtMost((width - PADDING) / (PAGE_WIDTH + PADDING))
|
||||||
context.matrices.translate(offsetX.toFloat(), offsetY.toFloat(), 0F)
|
.coerceAtLeast(1)
|
||||||
renderStoragePage(context, value, mouseX - offsetX, mouseY - offsetY)
|
measurements = Measurements()
|
||||||
context.matrices.pop()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
inline fun layoutedForEach(onEach: (data: Pair<StoragePageSlot, StorageData.StorageInventory>, offsetX: Int, offsetY: Int) -> Unit) {
|
|
||||||
var offsetY = 0
|
|
||||||
var currentMaxHeight = StorageOverlay.config.margin - StorageOverlay.config.padding - scroll
|
|
||||||
var totalHeight = -currentMaxHeight
|
|
||||||
content.storageInventories.onEachIndexed { index, (key, value) ->
|
|
||||||
val pageX = (index % StorageOverlay.config.rows)
|
|
||||||
if (pageX == 0) {
|
|
||||||
currentMaxHeight += StorageOverlay.config.padding
|
|
||||||
offsetY += currentMaxHeight
|
|
||||||
totalHeight += currentMaxHeight
|
|
||||||
currentMaxHeight = 0
|
|
||||||
}
|
|
||||||
val xPosition =
|
|
||||||
width / 2 - (StorageOverlay.config.rows * (pageWidth + StorageOverlay.config.padding) - StorageOverlay.config.padding) / 2 + pageX * (pageWidth + StorageOverlay.config.padding)
|
|
||||||
onEach(Pair(key, value), xPosition, offsetY)
|
|
||||||
val height = getStorePageHeight(value)
|
|
||||||
currentMaxHeight = max(currentMaxHeight, height)
|
|
||||||
}
|
|
||||||
lastRenderedHeight = totalHeight + currentMaxHeight
|
|
||||||
}
|
|
||||||
|
|
||||||
override fun mouseClicked(mouseX: Double, mouseY: Double, button: Int): Boolean {
|
|
||||||
layoutedForEach { (k, p), x, y ->
|
|
||||||
val rx = mouseX - x
|
|
||||||
val ry = mouseY - y
|
|
||||||
if (rx in (0.0..pageWidth.toDouble()) && ry in (0.0..getStorePageHeight(p).toDouble())) {
|
|
||||||
close()
|
|
||||||
StorageOverlay.lastStorageOverlay = this
|
|
||||||
k.navigateTo()
|
|
||||||
return true
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return super.mouseClicked(mouseX, mouseY, button)
|
|
||||||
}
|
|
||||||
|
|
||||||
fun getStorePageHeight(page: StorageData.StorageInventory): Int {
|
|
||||||
return page.inventory?.rows?.let { it * 19 + MC.font.fontHeight + 2 } ?: 60
|
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun mouseScrolled(
|
override fun mouseScrolled(
|
||||||
@@ -109,39 +78,233 @@ class StorageOverlayScreen() : Screen(Text.empty()) {
|
|||||||
horizontalAmount: Double,
|
horizontalAmount: Double,
|
||||||
verticalAmount: Double
|
verticalAmount: Double
|
||||||
): Boolean {
|
): Boolean {
|
||||||
scroll =
|
scroll = (scroll + StorageOverlay.adjustScrollSpeed(verticalAmount)).toFloat()
|
||||||
(scroll + verticalAmount * StorageOverlay.config.scrollSpeed *
|
.coerceAtMost(getMaxScroll())
|
||||||
(if (StorageOverlay.config.inverseScroll) 1 else -1)).toInt()
|
.coerceAtLeast(0F)
|
||||||
.coerceAtMost(lastRenderedHeight - height + 2 * StorageOverlay.config.margin).coerceAtLeast(0)
|
|
||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun renderStoragePage(context: DrawContext, page: StorageData.StorageInventory, mouseX: Int, mouseY: Int) {
|
fun getMaxScroll() = lastRenderedInnerHeight.toFloat() - getScrollPanelInner().height
|
||||||
context.drawText(MC.font, page.title, 2, 2, -1, true)
|
|
||||||
val inventory = page.inventory
|
|
||||||
if (inventory == null) {
|
|
||||||
// TODO: Missing texture
|
|
||||||
context.fill(0, 0, pageWidth, 60, DyeColor.RED.toShedaniel().darker(4.0).color)
|
|
||||||
context.drawCenteredTextWithShadow(MC.font, Text.literal("Not loaded yet"), pageWidth / 2, 30, -1)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
for ((index, stack) in inventory.stacks.withIndex()) {
|
val playerInventorySprite = Identifier.of("firmament:storageoverlay/player_inventory")
|
||||||
val x = (index % 9) * 19
|
val upperBackgroundSprite = Identifier.of("firmament:storageoverlay/upper_background")
|
||||||
val y = (index / 9) * 19 + MC.font.fontHeight + 2
|
val slotRowSprite = Identifier.of("firmament:storageoverlay/storage_row")
|
||||||
if (((mouseX - x) in 0 until 18) && ((mouseY - y) in 0 until 18)) {
|
val scrollbarBackground = Identifier.of("firmament:storageoverlay/scroll_bar_background")
|
||||||
context.fill(x, y, x + 18, y + 18, 0x80808080.toInt())
|
val scrollbarKnob = Identifier.of("firmament:storageoverlay/scroll_bar_knob")
|
||||||
} else {
|
|
||||||
context.fill(x, y, x + 18, y + 18, 0x40808080.toInt())
|
|
||||||
}
|
|
||||||
context.drawItem(stack, x + 1, y + 1)
|
|
||||||
context.drawItemInSlot(MC.font, stack, x + 1, y + 1)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
override fun close() {
|
override fun close() {
|
||||||
discardOldHandle()
|
isExiting = true
|
||||||
isClosing = true
|
|
||||||
super.close()
|
super.close()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
override fun render(context: DrawContext, mouseX: Int, mouseY: Int, delta: Float) {
|
||||||
|
super.render(context, mouseX, mouseY, delta)
|
||||||
|
drawBackgrounds(context)
|
||||||
|
drawPages(context, mouseX, mouseY, delta, null, null, Point())
|
||||||
|
drawScrollBar(context)
|
||||||
|
drawPlayerInventory(context, mouseX, mouseY, delta)
|
||||||
|
}
|
||||||
|
|
||||||
|
fun getScrollbarPercentage(): Float {
|
||||||
|
return scroll / getMaxScroll()
|
||||||
|
}
|
||||||
|
|
||||||
|
fun drawScrollBar(context: DrawContext) {
|
||||||
|
val sbRect = getScrollBarRect()
|
||||||
|
context.drawGuiTexture(
|
||||||
|
scrollbarBackground,
|
||||||
|
sbRect.minX, sbRect.minY,
|
||||||
|
sbRect.width, sbRect.height,
|
||||||
|
)
|
||||||
|
context.drawGuiTexture(
|
||||||
|
scrollbarKnob,
|
||||||
|
sbRect.minX, sbRect.minY + (getScrollbarPercentage() * (sbRect.height - SCROLL_BAR_HEIGHT)).toInt(),
|
||||||
|
SCROLL_BAR_WIDTH, SCROLL_BAR_HEIGHT
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
fun drawBackgrounds(context: DrawContext) {
|
||||||
|
context.drawGuiTexture(upperBackgroundSprite,
|
||||||
|
measurements.x,
|
||||||
|
measurements.y,
|
||||||
|
0,
|
||||||
|
measurements.overviewWidth,
|
||||||
|
measurements.overviewHeight)
|
||||||
|
context.drawGuiTexture(playerInventorySprite,
|
||||||
|
measurements.playerX,
|
||||||
|
measurements.playerY,
|
||||||
|
0,
|
||||||
|
PLAYER_WIDTH,
|
||||||
|
PLAYER_HEIGHT)
|
||||||
|
}
|
||||||
|
|
||||||
|
fun getPlayerInventorySlotPosition(int: Int): Pair<Int, Int> {
|
||||||
|
if (int < 9) {
|
||||||
|
return Pair(measurements.playerX + int * SLOT_SIZE + HOTBAR_X, HOTBAR_Y + measurements.playerY)
|
||||||
|
}
|
||||||
|
return Pair(
|
||||||
|
measurements.playerX + (int % 9) * SLOT_SIZE + HOTBAR_X,
|
||||||
|
measurements.playerY + (int / 9 - 1) * SLOT_SIZE + MAIN_INVENTORY_Y
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
fun drawPlayerInventory(context: DrawContext, mouseX: Int, mouseY: Int, delta: Float) {
|
||||||
|
val items = MC.player?.inventory?.main ?: return
|
||||||
|
items.withIndex().forEach { (index, item) ->
|
||||||
|
val (x, y) = getPlayerInventorySlotPosition(index)
|
||||||
|
context.drawItem(item, x, y, 0)
|
||||||
|
context.drawItemInSlot(textRenderer, item, x, y)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fun getScrollBarRect(): Rectangle {
|
||||||
|
return Rectangle(measurements.x + PADDING + measurements.innerScrollPanelWidth + PADDING,
|
||||||
|
measurements.y + PADDING,
|
||||||
|
SCROLL_BAR_WIDTH,
|
||||||
|
measurements.innerScrollPanelHeight)
|
||||||
|
}
|
||||||
|
|
||||||
|
fun getScrollPanelInner(): Rectangle {
|
||||||
|
return Rectangle(measurements.x + PADDING,
|
||||||
|
measurements.y + PADDING,
|
||||||
|
measurements.innerScrollPanelWidth,
|
||||||
|
measurements.innerScrollPanelHeight)
|
||||||
|
}
|
||||||
|
|
||||||
|
fun createScissors(context: DrawContext) {
|
||||||
|
val rect = getScrollPanelInner()
|
||||||
|
context.enableScissor(
|
||||||
|
rect.minX, rect.minY,
|
||||||
|
rect.maxX, rect.maxY
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
fun drawPages(
|
||||||
|
context: DrawContext, mouseX: Int, mouseY: Int, delta: Float,
|
||||||
|
excluding: StoragePageSlot?,
|
||||||
|
slots: List<Slot>?,
|
||||||
|
slotOffset: Point
|
||||||
|
) {
|
||||||
|
createScissors(context)
|
||||||
|
val data = StorageOverlay.Data.data ?: StorageData()
|
||||||
|
layoutedForEach(data) { rect, page, inventory ->
|
||||||
|
drawPage(context,
|
||||||
|
rect.x,
|
||||||
|
rect.y,
|
||||||
|
page, inventory,
|
||||||
|
if (excluding == page) slots else null,
|
||||||
|
slotOffset
|
||||||
|
)
|
||||||
|
}
|
||||||
|
context.disableScissor()
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun mouseClicked(mouseX: Double, mouseY: Double, button: Int): Boolean {
|
||||||
|
return mouseClicked(mouseX, mouseY, button, null)
|
||||||
|
}
|
||||||
|
|
||||||
|
fun mouseClicked(mouseX: Double, mouseY: Double, button: Int, activePage: StoragePageSlot?): Boolean {
|
||||||
|
if (getScrollPanelInner().contains(mouseX, mouseY)) {
|
||||||
|
val data = StorageOverlay.Data.data ?: StorageData()
|
||||||
|
layoutedForEach(data) { rect, page, _ ->
|
||||||
|
if (rect.contains(mouseX, mouseY) && activePage != page && button == 0) {
|
||||||
|
page.navigateTo()
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
val sbRect = getScrollBarRect()
|
||||||
|
if (sbRect.contains(mouseX, mouseY)) {
|
||||||
|
// TODO: support dragging of the mouse and such
|
||||||
|
val percentage = (mouseY - sbRect.getY()) / sbRect.getHeight()
|
||||||
|
scroll = (getMaxScroll() * percentage).toFloat()
|
||||||
|
mouseScrolled(0.0, 0.0, 0.0, 0.0)
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
private inline fun layoutedForEach(
|
||||||
|
data: StorageData,
|
||||||
|
func: (
|
||||||
|
rectangle: Rectangle,
|
||||||
|
page: StoragePageSlot, inventory: StorageData.StorageInventory,
|
||||||
|
) -> Unit
|
||||||
|
) {
|
||||||
|
var yOffset = -scroll.toInt()
|
||||||
|
var xOffset = 0
|
||||||
|
var maxHeight = 0
|
||||||
|
for ((page, inventory) in data.storageInventories.entries) {
|
||||||
|
val currentHeight = inventory.inventory?.let { it.rows * SLOT_SIZE + 4 + textRenderer.fontHeight }
|
||||||
|
?: 18
|
||||||
|
maxHeight = maxOf(maxHeight, currentHeight)
|
||||||
|
val rect = Rectangle(
|
||||||
|
measurements.x + PADDING + (PAGE_WIDTH + PADDING) * xOffset,
|
||||||
|
yOffset + measurements.y + PADDING,
|
||||||
|
PAGE_WIDTH,
|
||||||
|
currentHeight
|
||||||
|
)
|
||||||
|
func(rect, page, inventory)
|
||||||
|
xOffset++
|
||||||
|
if (xOffset >= pageWidthCount) {
|
||||||
|
yOffset += maxHeight
|
||||||
|
xOffset = 0
|
||||||
|
maxHeight = 0
|
||||||
|
}
|
||||||
|
}
|
||||||
|
lastRenderedInnerHeight = maxHeight + yOffset + scroll.toInt()
|
||||||
|
}
|
||||||
|
|
||||||
|
fun drawPage(
|
||||||
|
context: DrawContext,
|
||||||
|
x: Int,
|
||||||
|
y: Int,
|
||||||
|
page: StoragePageSlot,
|
||||||
|
inventory: StorageData.StorageInventory,
|
||||||
|
slots: List<Slot>?,
|
||||||
|
slotOffset: Point,
|
||||||
|
): Int {
|
||||||
|
val inv = inventory.inventory
|
||||||
|
if (inv == null) {
|
||||||
|
context.drawGuiTexture(upperBackgroundSprite, x, y, PAGE_WIDTH, 18)
|
||||||
|
context.drawText(textRenderer,
|
||||||
|
Text.literal("TODO: open this page"),
|
||||||
|
x + 4,
|
||||||
|
y + 4,
|
||||||
|
-1,
|
||||||
|
true)
|
||||||
|
return 18
|
||||||
|
}
|
||||||
|
assertTrueOr(slots == null || slots.size == inv.stacks.size) { return 0 }
|
||||||
|
val name = page.defaultName()
|
||||||
|
context.drawText(textRenderer, Text.literal(name), x + 4, y + 2,
|
||||||
|
if (slots == null) 0xFFFFFFFF.toInt() else 0xFFFFFF00.toInt(), true)
|
||||||
|
context.drawGuiTexture(slotRowSprite, x, y + 4 + textRenderer.fontHeight, PAGE_WIDTH, inv.rows * SLOT_SIZE)
|
||||||
|
inv.stacks.forEachIndexed { index, stack ->
|
||||||
|
val slotX = (index % 9) * SLOT_SIZE + x + 1
|
||||||
|
val slotY = (index / 9) * SLOT_SIZE + y + 4 + textRenderer.fontHeight + 1
|
||||||
|
if (slots == null) {
|
||||||
|
context.drawItem(stack, slotX, slotY)
|
||||||
|
context.drawItemInSlot(textRenderer, stack, slotX, slotY)
|
||||||
|
} else {
|
||||||
|
val slot = slots[index]
|
||||||
|
slot.x = slotX - slotOffset.x
|
||||||
|
slot.y = slotY - slotOffset.y
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return inv.rows * SLOT_SIZE + 4 + textRenderer.fontHeight
|
||||||
|
}
|
||||||
|
|
||||||
|
fun getBounds(): List<Rectangle> {
|
||||||
|
return listOf(
|
||||||
|
Rectangle(measurements.x,
|
||||||
|
measurements.y,
|
||||||
|
measurements.overviewWidth,
|
||||||
|
measurements.overviewHeight),
|
||||||
|
Rectangle(measurements.playerX,
|
||||||
|
measurements.playerY,
|
||||||
|
PLAYER_WIDTH,
|
||||||
|
PLAYER_HEIGHT))
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -0,0 +1,126 @@
|
|||||||
|
/*
|
||||||
|
* SPDX-FileCopyrightText: 2023 Linnea Gräf <nea@nea.moe>
|
||||||
|
* SPDX-FileCopyrightText: 2024 Linnea Gräf <nea@nea.moe>
|
||||||
|
*
|
||||||
|
* SPDX-License-Identifier: GPL-3.0-or-later
|
||||||
|
*/
|
||||||
|
|
||||||
|
package moe.nea.firmament.features.inventory.storageoverlay
|
||||||
|
|
||||||
|
import kotlin.math.max
|
||||||
|
import net.minecraft.block.Blocks
|
||||||
|
import net.minecraft.client.gui.DrawContext
|
||||||
|
import net.minecraft.client.gui.screen.Screen
|
||||||
|
import net.minecraft.item.Item
|
||||||
|
import net.minecraft.item.Items
|
||||||
|
import net.minecraft.text.Text
|
||||||
|
import net.minecraft.util.DyeColor
|
||||||
|
import moe.nea.firmament.util.MC
|
||||||
|
import moe.nea.firmament.util.toShedaniel
|
||||||
|
|
||||||
|
class StorageOverviewScreen() : Screen(Text.empty()) {
|
||||||
|
companion object {
|
||||||
|
val emptyStorageSlotItems = listOf<Item>(
|
||||||
|
Blocks.RED_STAINED_GLASS_PANE.asItem(),
|
||||||
|
Blocks.BROWN_STAINED_GLASS_PANE.asItem(),
|
||||||
|
Items.GRAY_DYE
|
||||||
|
)
|
||||||
|
val pageWidth get() = 19 * 9
|
||||||
|
}
|
||||||
|
|
||||||
|
val content = StorageOverlay.Data.data ?: StorageData()
|
||||||
|
var isClosing = false
|
||||||
|
|
||||||
|
var scroll = 0
|
||||||
|
var lastRenderedHeight = 0
|
||||||
|
|
||||||
|
override fun render(context: DrawContext, mouseX: Int, mouseY: Int, delta: Float) {
|
||||||
|
super.render(context, mouseX, mouseY, delta)
|
||||||
|
context.fill(0, 0, width, height, 0x90000000.toInt())
|
||||||
|
layoutedForEach { (key, value), offsetX, offsetY ->
|
||||||
|
context.matrices.push()
|
||||||
|
context.matrices.translate(offsetX.toFloat(), offsetY.toFloat(), 0F)
|
||||||
|
renderStoragePage(context, value, mouseX - offsetX, mouseY - offsetY)
|
||||||
|
context.matrices.pop()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
inline fun layoutedForEach(onEach: (data: Pair<StoragePageSlot, StorageData.StorageInventory>, offsetX: Int, offsetY: Int) -> Unit) {
|
||||||
|
var offsetY = 0
|
||||||
|
var currentMaxHeight = StorageOverlay.config.margin - StorageOverlay.config.padding - scroll
|
||||||
|
var totalHeight = -currentMaxHeight
|
||||||
|
content.storageInventories.onEachIndexed { index, (key, value) ->
|
||||||
|
val pageX = (index % StorageOverlay.config.rows)
|
||||||
|
if (pageX == 0) {
|
||||||
|
currentMaxHeight += StorageOverlay.config.padding
|
||||||
|
offsetY += currentMaxHeight
|
||||||
|
totalHeight += currentMaxHeight
|
||||||
|
currentMaxHeight = 0
|
||||||
|
}
|
||||||
|
val xPosition =
|
||||||
|
width / 2 - (StorageOverlay.config.rows * (pageWidth + StorageOverlay.config.padding) - StorageOverlay.config.padding) / 2 + pageX * (pageWidth + StorageOverlay.config.padding)
|
||||||
|
onEach(Pair(key, value), xPosition, offsetY)
|
||||||
|
val height = getStorePageHeight(value)
|
||||||
|
currentMaxHeight = max(currentMaxHeight, height)
|
||||||
|
}
|
||||||
|
lastRenderedHeight = totalHeight + currentMaxHeight
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun mouseClicked(mouseX: Double, mouseY: Double, button: Int): Boolean {
|
||||||
|
layoutedForEach { (k, p), x, y ->
|
||||||
|
val rx = mouseX - x
|
||||||
|
val ry = mouseY - y
|
||||||
|
if (rx in (0.0..pageWidth.toDouble()) && ry in (0.0..getStorePageHeight(p).toDouble())) {
|
||||||
|
close()
|
||||||
|
StorageOverlay.lastStorageOverlay = this
|
||||||
|
k.navigateTo()
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return super.mouseClicked(mouseX, mouseY, button)
|
||||||
|
}
|
||||||
|
|
||||||
|
fun getStorePageHeight(page: StorageData.StorageInventory): Int {
|
||||||
|
return page.inventory?.rows?.let { it * 19 + MC.font.fontHeight + 2 } ?: 60
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun mouseScrolled(
|
||||||
|
mouseX: Double,
|
||||||
|
mouseY: Double,
|
||||||
|
horizontalAmount: Double,
|
||||||
|
verticalAmount: Double
|
||||||
|
): Boolean {
|
||||||
|
scroll =
|
||||||
|
(scroll + StorageOverlay.adjustScrollSpeed(verticalAmount)).toInt()
|
||||||
|
.coerceAtMost(lastRenderedHeight - height + 2 * StorageOverlay.config.margin).coerceAtLeast(0)
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun renderStoragePage(context: DrawContext, page: StorageData.StorageInventory, mouseX: Int, mouseY: Int) {
|
||||||
|
context.drawText(MC.font, page.title, 2, 2, -1, true)
|
||||||
|
val inventory = page.inventory
|
||||||
|
if (inventory == null) {
|
||||||
|
// TODO: Missing texture
|
||||||
|
context.fill(0, 0, pageWidth, 60, DyeColor.RED.toShedaniel().darker(4.0).color)
|
||||||
|
context.drawCenteredTextWithShadow(MC.font, Text.literal("Not loaded yet"), pageWidth / 2, 30, -1)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
for ((index, stack) in inventory.stacks.withIndex()) {
|
||||||
|
val x = (index % 9) * 19
|
||||||
|
val y = (index / 9) * 19 + MC.font.fontHeight + 2
|
||||||
|
if (((mouseX - x) in 0 until 18) && ((mouseY - y) in 0 until 18)) {
|
||||||
|
context.fill(x, y, x + 18, y + 18, 0x80808080.toInt())
|
||||||
|
} else {
|
||||||
|
context.fill(x, y, x + 18, y + 18, 0x40808080.toInt())
|
||||||
|
}
|
||||||
|
context.drawItem(stack, x + 1, y + 1)
|
||||||
|
context.drawItemInSlot(MC.font, stack, x + 1, y + 1)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun close() {
|
||||||
|
isClosing = true
|
||||||
|
super.close()
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -19,20 +19,21 @@ import me.shedaniel.rei.api.client.registry.transfer.TransferHandlerRegistry
|
|||||||
import me.shedaniel.rei.api.common.entry.EntryStack
|
import me.shedaniel.rei.api.common.entry.EntryStack
|
||||||
import me.shedaniel.rei.api.common.entry.type.EntryTypeRegistry
|
import me.shedaniel.rei.api.common.entry.type.EntryTypeRegistry
|
||||||
import me.shedaniel.rei.api.common.entry.type.VanillaEntryTypes
|
import me.shedaniel.rei.api.common.entry.type.VanillaEntryTypes
|
||||||
import moe.nea.firmament.events.HandledScreenPushREIEvent
|
|
||||||
import moe.nea.firmament.features.inventory.CraftingOverlay
|
|
||||||
import moe.nea.firmament.rei.recipes.SBCraftingRecipe
|
|
||||||
import moe.nea.firmament.rei.recipes.SBForgeRecipe
|
|
||||||
import moe.nea.firmament.repo.RepoManager
|
|
||||||
import moe.nea.firmament.util.SkyblockId
|
|
||||||
import moe.nea.firmament.util.skyblockId
|
|
||||||
import moe.nea.firmament.util.unformattedString
|
|
||||||
import net.minecraft.client.gui.screen.ingame.GenericContainerScreen
|
import net.minecraft.client.gui.screen.ingame.GenericContainerScreen
|
||||||
import net.minecraft.client.gui.screen.ingame.HandledScreen
|
import net.minecraft.client.gui.screen.ingame.HandledScreen
|
||||||
import net.minecraft.item.ItemStack
|
import net.minecraft.item.ItemStack
|
||||||
import net.minecraft.text.Text
|
import net.minecraft.text.Text
|
||||||
import net.minecraft.util.Identifier
|
import net.minecraft.util.Identifier
|
||||||
|
import moe.nea.firmament.events.HandledScreenPushREIEvent
|
||||||
|
import moe.nea.firmament.features.inventory.CraftingOverlay
|
||||||
|
import moe.nea.firmament.features.inventory.storageoverlay.StorageOverlayScreen
|
||||||
|
import moe.nea.firmament.rei.recipes.SBCraftingRecipe
|
||||||
|
import moe.nea.firmament.rei.recipes.SBForgeRecipe
|
||||||
import moe.nea.firmament.rei.recipes.SBMobDropRecipe
|
import moe.nea.firmament.rei.recipes.SBMobDropRecipe
|
||||||
|
import moe.nea.firmament.repo.RepoManager
|
||||||
|
import moe.nea.firmament.util.SkyblockId
|
||||||
|
import moe.nea.firmament.util.skyblockId
|
||||||
|
import moe.nea.firmament.util.unformattedString
|
||||||
|
|
||||||
|
|
||||||
class FirmamentReiPlugin : REIClientPlugin {
|
class FirmamentReiPlugin : REIClientPlugin {
|
||||||
@@ -44,6 +45,7 @@ class FirmamentReiPlugin : REIClientPlugin {
|
|||||||
|
|
||||||
val SKYBLOCK_ITEM_TYPE_ID = Identifier.of("firmament", "skyblockitems")
|
val SKYBLOCK_ITEM_TYPE_ID = Identifier.of("firmament", "skyblockitems")
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun registerTransferHandlers(registry: TransferHandlerRegistry) {
|
override fun registerTransferHandlers(registry: TransferHandlerRegistry) {
|
||||||
registry.register(TransferHandler { context ->
|
registry.register(TransferHandler { context ->
|
||||||
val screen = context.containerScreen
|
val screen = context.containerScreen
|
||||||
@@ -69,6 +71,7 @@ class FirmamentReiPlugin : REIClientPlugin {
|
|||||||
|
|
||||||
override fun registerExclusionZones(zones: ExclusionZones) {
|
override fun registerExclusionZones(zones: ExclusionZones) {
|
||||||
zones.register(HandledScreen::class.java) { HandledScreenPushREIEvent.publish(HandledScreenPushREIEvent(it)).rectangles }
|
zones.register(HandledScreen::class.java) { HandledScreenPushREIEvent.publish(HandledScreenPushREIEvent(it)).rectangles }
|
||||||
|
zones.register(StorageOverlayScreen::class.java) { it.getBounds() }
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun registerDisplays(registry: DisplayRegistry) {
|
override fun registerDisplays(registry: DisplayRegistry) {
|
||||||
@@ -80,7 +83,8 @@ class FirmamentReiPlugin : REIClientPlugin {
|
|||||||
SBForgeRecipe.Category.categoryIdentifier,
|
SBForgeRecipe.Category.categoryIdentifier,
|
||||||
SkyblockForgeRecipeDynamicGenerator
|
SkyblockForgeRecipeDynamicGenerator
|
||||||
)
|
)
|
||||||
registry.registerDisplayGenerator(SBMobDropRecipe.Category.categoryIdentifier, SkyblockMobDropRecipeDynamicGenerator)
|
registry.registerDisplayGenerator(SBMobDropRecipe.Category.categoryIdentifier,
|
||||||
|
SkyblockMobDropRecipeDynamicGenerator)
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun registerCollapsibleEntries(registry: CollapsibleEntryRegistry) {
|
override fun registerCollapsibleEntries(registry: CollapsibleEntryRegistry) {
|
||||||
|
|||||||
@@ -0,0 +1,19 @@
|
|||||||
|
/*
|
||||||
|
* SPDX-FileCopyrightText: 2024 Linnea Gräf <nea@nea.moe>
|
||||||
|
*
|
||||||
|
* SPDX-License-Identifier: GPL-3.0-or-later
|
||||||
|
*/
|
||||||
|
|
||||||
|
package moe.nea.firmament.util.customgui
|
||||||
|
|
||||||
|
import net.minecraft.screen.slot.Slot
|
||||||
|
|
||||||
|
interface CoordRememberingSlot {
|
||||||
|
fun rememberCoords_firmament()
|
||||||
|
fun restoreCoords_firmament()
|
||||||
|
fun getOriginalX_firmament(): Int
|
||||||
|
fun getOriginalY_firmament(): Int
|
||||||
|
}
|
||||||
|
|
||||||
|
val Slot.originalX get() = (this as CoordRememberingSlot).getOriginalX_firmament()
|
||||||
|
val Slot.originalY get() = (this as CoordRememberingSlot).getOriginalY_firmament()
|
||||||
@@ -0,0 +1,77 @@
|
|||||||
|
/*
|
||||||
|
* SPDX-FileCopyrightText: 2024 Linnea Gräf <nea@nea.moe>
|
||||||
|
*
|
||||||
|
* SPDX-License-Identifier: GPL-3.0-or-later
|
||||||
|
*/
|
||||||
|
|
||||||
|
package moe.nea.firmament.util.customgui
|
||||||
|
|
||||||
|
import me.shedaniel.math.Rectangle
|
||||||
|
import net.minecraft.client.gui.DrawContext
|
||||||
|
import net.minecraft.screen.slot.Slot
|
||||||
|
import moe.nea.firmament.annotations.Subscribe
|
||||||
|
import moe.nea.firmament.events.HandledScreenPushREIEvent
|
||||||
|
|
||||||
|
abstract class CustomGui {
|
||||||
|
|
||||||
|
abstract fun getBounds(): List<Rectangle>
|
||||||
|
|
||||||
|
open fun moveSlot(slot: Slot) {
|
||||||
|
// TODO: return a Pair maybe? worth an investigation
|
||||||
|
}
|
||||||
|
|
||||||
|
companion object {
|
||||||
|
@Subscribe
|
||||||
|
fun onExclusionZone(event: HandledScreenPushREIEvent) {
|
||||||
|
val customGui = event.screen.customGui ?: return
|
||||||
|
event.rectangles.addAll(customGui.getBounds())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
open fun render(
|
||||||
|
drawContext: DrawContext,
|
||||||
|
delta: Float,
|
||||||
|
mouseX: Int,
|
||||||
|
mouseY: Int
|
||||||
|
) {
|
||||||
|
}
|
||||||
|
|
||||||
|
open fun mouseClick(mouseX: Double, mouseY: Double, button: Int): Boolean {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
open fun afterSlotRender(context: DrawContext, slot: Slot) {}
|
||||||
|
open fun beforeSlotRender(context: DrawContext, slot: Slot) {}
|
||||||
|
open fun mouseScrolled(mouseX: Double, mouseY: Double, horizontalAmount: Double, verticalAmount: Double): Boolean {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
open fun isClickOutsideBounds(mouseX: Double, mouseY: Double): Boolean {
|
||||||
|
return getBounds().none { it.contains(mouseX, mouseY) }
|
||||||
|
}
|
||||||
|
|
||||||
|
open fun isPointWithinBounds(
|
||||||
|
x: Int,
|
||||||
|
y: Int,
|
||||||
|
width: Int,
|
||||||
|
height: Int,
|
||||||
|
pointX: Double,
|
||||||
|
pointY: Double,
|
||||||
|
): Boolean {
|
||||||
|
return getBounds().any { it.contains(pointX, pointY) } &&
|
||||||
|
Rectangle(x, y, width, height).contains(pointX, pointY)
|
||||||
|
}
|
||||||
|
|
||||||
|
open fun isPointOverSlot(slot: Slot, xOffset: Int, yOffset: Int, pointX: Double, pointY: Double): Boolean {
|
||||||
|
return isPointWithinBounds(slot.x + xOffset, slot.y + yOffset, 16, 16, pointX, pointY)
|
||||||
|
}
|
||||||
|
|
||||||
|
open fun onInit() {}
|
||||||
|
open fun shouldDrawForeground(): Boolean {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
||||||
|
open fun onVoluntaryExit(): Boolean {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,22 @@
|
|||||||
|
/*
|
||||||
|
* SPDX-FileCopyrightText: 2024 Linnea Gräf <nea@nea.moe>
|
||||||
|
*
|
||||||
|
* SPDX-License-Identifier: GPL-3.0-or-later
|
||||||
|
*/
|
||||||
|
|
||||||
|
package moe.nea.firmament.util.customgui
|
||||||
|
|
||||||
|
import net.minecraft.client.gui.screen.ingame.HandledScreen
|
||||||
|
|
||||||
|
@Suppress("FunctionName")
|
||||||
|
interface HasCustomGui {
|
||||||
|
fun getCustomGui_Firmament(): CustomGui?
|
||||||
|
fun setCustomGui_Firmament(gui: CustomGui?)
|
||||||
|
}
|
||||||
|
|
||||||
|
var <T : HandledScreen<*>> T.customGui: CustomGui?
|
||||||
|
get() = (this as HasCustomGui).getCustomGui_Firmament()
|
||||||
|
set(value) {
|
||||||
|
(this as HasCustomGui).setCustomGui_Firmament(value)
|
||||||
|
}
|
||||||
|
|
||||||
Binary file not shown.
|
After Width: | Height: | Size: 1019 B |
@@ -0,0 +1,3 @@
|
|||||||
|
SPDX-FileCopyrightText: 2024 Linnea Gräf <nea@nea.moe>
|
||||||
|
SPDX-FileCopyrightText: This texture is a derivative of textures from Minecraft by Mojang.
|
||||||
|
SPDX-License-Identifier: CC0-1.0
|
||||||
Binary file not shown.
|
After Width: | Height: | Size: 4.2 KiB |
@@ -0,0 +1,3 @@
|
|||||||
|
SPDX-FileCopyrightText: 2024 Linnea Gräf <nea@nea.moe>
|
||||||
|
SPDX-FileCopyrightText: This texture is a derivative of textures from Minecraft by Mojang.
|
||||||
|
SPDX-License-Identifier: CC0-1.0
|
||||||
@@ -0,0 +1,10 @@
|
|||||||
|
{
|
||||||
|
"gui": {
|
||||||
|
"scaling": {
|
||||||
|
"type": "nine_slice",
|
||||||
|
"width": 17,
|
||||||
|
"height": 18,
|
||||||
|
"border": 2
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,3 @@
|
|||||||
|
SPDX-FileCopyrightText: 2024 Linnea Gräf <nea@nea.moe>
|
||||||
|
SPDX-FileCopyrightText: This texture is a derivative of textures from Minecraft by Mojang.
|
||||||
|
SPDX-License-Identifier: CC0-1.0
|
||||||
Binary file not shown.
|
After Width: | Height: | Size: 4.5 KiB |
@@ -0,0 +1,2 @@
|
|||||||
|
SPDX-FileCopyrightText: 2024 Linnea Gräf <nea@nea.moe>
|
||||||
|
SPDX-License-Identifier: CC0-1.0
|
||||||
Binary file not shown.
|
After Width: | Height: | Size: 649 B |
@@ -0,0 +1,3 @@
|
|||||||
|
SPDX-FileCopyrightText: 2024 Linnea Gräf <nea@nea.moe>
|
||||||
|
SPDX-FileCopyrightText: This texture is a derivative of textures from Minecraft by Mojang.
|
||||||
|
SPDX-License-Identifier: CC0-1.0
|
||||||
@@ -0,0 +1,9 @@
|
|||||||
|
{
|
||||||
|
"gui": {
|
||||||
|
"scaling": {
|
||||||
|
"type": "tile",
|
||||||
|
"width": 162,
|
||||||
|
"height": 18
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,3 @@
|
|||||||
|
SPDX-FileCopyrightText: 2024 Linnea Gräf <nea@nea.moe>
|
||||||
|
SPDX-FileCopyrightText: This texture is a derivative of textures from Minecraft by Mojang.
|
||||||
|
SPDX-License-Identifier: CC0-1.0
|
||||||
Binary file not shown.
|
After Width: | Height: | Size: 1.4 KiB |
@@ -0,0 +1,3 @@
|
|||||||
|
SPDX-FileCopyrightText: 2024 Linnea Gräf <nea@nea.moe>
|
||||||
|
SPDX-FileCopyrightText: This texture is a derivative of textures from Minecraft by Mojang.
|
||||||
|
SPDX-License-Identifier: CC0-1.0
|
||||||
@@ -0,0 +1,10 @@
|
|||||||
|
{
|
||||||
|
"gui": {
|
||||||
|
"scaling": {
|
||||||
|
"type": "nine_slice",
|
||||||
|
"width": 176,
|
||||||
|
"height": 222,
|
||||||
|
"border": 10
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,3 @@
|
|||||||
|
SPDX-FileCopyrightText: 2024 Linnea Gräf <nea@nea.moe>
|
||||||
|
SPDX-FileCopyrightText: This texture is a derivative of textures from Minecraft by Mojang.
|
||||||
|
SPDX-License-Identifier: CC0-1.0
|
||||||
@@ -13,3 +13,7 @@ accessible field net/minecraft/entity/passive/AbstractHorseEntity SADDLED_FLAG I
|
|||||||
accessible field net/minecraft/entity/passive/AbstractHorseEntity HORSE_FLAGS Lnet/minecraft/entity/data/TrackedData;
|
accessible field net/minecraft/entity/passive/AbstractHorseEntity HORSE_FLAGS Lnet/minecraft/entity/data/TrackedData;
|
||||||
accessible method net/minecraft/resource/NamespaceResourceManager loadMetadata (Lnet/minecraft/resource/InputSupplier;)Lnet/minecraft/resource/metadata/ResourceMetadata;
|
accessible method net/minecraft/resource/NamespaceResourceManager loadMetadata (Lnet/minecraft/resource/InputSupplier;)Lnet/minecraft/resource/metadata/ResourceMetadata;
|
||||||
accessible method net/minecraft/client/gui/DrawContext drawTexturedQuad (Lnet/minecraft/util/Identifier;IIIIIFFFFFFFF)V
|
accessible method net/minecraft/client/gui/DrawContext drawTexturedQuad (Lnet/minecraft/util/Identifier;IIIIIFFFFFFFF)V
|
||||||
|
|
||||||
|
mutable field net/minecraft/screen/slot/Slot x I
|
||||||
|
mutable field net/minecraft/screen/slot/Slot y I
|
||||||
|
|
||||||
|
|||||||
@@ -81,7 +81,7 @@ class SubscribeAnnotationProcessor(
|
|||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
val parent = element.parentDeclaration
|
val parent = element.parentDeclaration
|
||||||
if (parent !is KSClassDeclaration || parent.isCompanionObject || parent.classKind != ClassKind.OBJECT) {
|
if (parent !is KSClassDeclaration || parent.classKind != ClassKind.OBJECT) {
|
||||||
logger.error("@Subscribe on a non-object", element)
|
logger.error("@Subscribe on a non-object", element)
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user