Add slot locking
This commit is contained in:
3
TODO.txt
3
TODO.txt
@@ -5,7 +5,6 @@
|
||||
- Storage Overlay
|
||||
- PV
|
||||
- NEU buttons
|
||||
- Slot locking
|
||||
- Pet/Equipment hud in inventory
|
||||
- Pet Overlay
|
||||
- Price Graphs
|
||||
@@ -27,6 +26,8 @@ Priority 2:
|
||||
- biome wands
|
||||
- dirt wand
|
||||
- block zapper
|
||||
- slot binding
|
||||
- uuid locking (allow items with that uuid to be moved around but not dropped/sold)
|
||||
|
||||
Priority 3:
|
||||
- Item rarity halo
|
||||
|
||||
@@ -0,0 +1,25 @@
|
||||
package moe.nea.firmament.mixins;
|
||||
|
||||
import moe.nea.firmament.events.IsSlotProtectedEvent;
|
||||
import net.minecraft.client.network.ClientPlayerEntity;
|
||||
import net.minecraft.entity.player.PlayerEntity;
|
||||
import net.minecraft.screen.slot.Slot;
|
||||
import org.spongepowered.asm.mixin.Mixin;
|
||||
import org.spongepowered.asm.mixin.injection.At;
|
||||
import org.spongepowered.asm.mixin.injection.Inject;
|
||||
import org.spongepowered.asm.mixin.injection.callback.CallbackInfoReturnable;
|
||||
|
||||
@Mixin(ClientPlayerEntity.class)
|
||||
public abstract class MixinClientPlayerEntity extends PlayerEntity {
|
||||
public MixinClientPlayerEntity() {
|
||||
super(null, null, 0, null);
|
||||
}
|
||||
|
||||
@Inject(method = "dropSelectedItem", at = @At("HEAD"), cancellable = true)
|
||||
public void onDropSelectedItem(boolean entireStack, CallbackInfoReturnable<Boolean> cir) {
|
||||
Slot fakeSlot = new Slot(getInventory(), getInventory().selectedSlot, 0, 0);
|
||||
if (IsSlotProtectedEvent.shouldBlockInteraction(fakeSlot)) {
|
||||
cir.setReturnValue(false);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,50 @@
|
||||
package moe.nea.firmament.mixins;
|
||||
|
||||
import moe.nea.firmament.events.HandledScreenKeyPressedEvent;
|
||||
import moe.nea.firmament.events.IsSlotProtectedEvent;
|
||||
import moe.nea.firmament.events.SlotRenderEvents;
|
||||
import net.minecraft.client.gui.screen.ingame.HandledScreen;
|
||||
import net.minecraft.client.util.math.MatrixStack;
|
||||
import net.minecraft.screen.slot.Slot;
|
||||
import net.minecraft.screen.slot.SlotActionType;
|
||||
import org.spongepowered.asm.mixin.Mixin;
|
||||
import org.spongepowered.asm.mixin.injection.At;
|
||||
import org.spongepowered.asm.mixin.injection.Inject;
|
||||
import org.spongepowered.asm.mixin.injection.callback.CallbackInfo;
|
||||
import org.spongepowered.asm.mixin.injection.callback.CallbackInfoReturnable;
|
||||
import org.spongepowered.asm.mixin.injection.callback.LocalCapture;
|
||||
|
||||
@Mixin(HandledScreen.class)
|
||||
public class MixinHandledScreen {
|
||||
|
||||
@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) {
|
||||
if (HandledScreenKeyPressedEvent.Companion.publish(new HandledScreenKeyPressedEvent(keyCode, scanCode, modifiers)).getCancelled()) {
|
||||
cir.setReturnValue(true);
|
||||
}
|
||||
}
|
||||
|
||||
@Inject(method = "onMouseClick(Lnet/minecraft/screen/slot/Slot;IILnet/minecraft/screen/slot/SlotActionType;)V", at = @At("HEAD"), cancellable = true)
|
||||
public void onMouseClickedSlot(Slot slot, int slotId, int button, SlotActionType actionType, CallbackInfo ci) {
|
||||
if (IsSlotProtectedEvent.shouldBlockInteraction(slot)) {
|
||||
ci.cancel();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@Inject(method = "render", at = @At(value = "INVOKE", target = "Lnet/minecraft/client/gui/screen/ingame/HandledScreen;drawSlot(Lnet/minecraft/client/util/math/MatrixStack;Lnet/minecraft/screen/slot/Slot;)V", shift = At.Shift.AFTER), locals = LocalCapture.CAPTURE_FAILHARD)
|
||||
public void onAfterDrawSlot(
|
||||
MatrixStack matrices, int mouseX, int mouseY, float delta, CallbackInfo ci,
|
||||
int i, int j, int k, Slot slot) {
|
||||
SlotRenderEvents.After event = new SlotRenderEvents.After(matrices, slot, mouseX, mouseY, delta);
|
||||
SlotRenderEvents.After.Companion.publish(event);
|
||||
}
|
||||
|
||||
@Inject(method = "render", at = @At(value = "INVOKE", target = "Lnet/minecraft/client/gui/screen/ingame/HandledScreen;drawSlot(Lnet/minecraft/client/util/math/MatrixStack;Lnet/minecraft/screen/slot/Slot;)V", shift = At.Shift.BEFORE), locals = LocalCapture.CAPTURE_FAILHARD)
|
||||
public void onBeforeDrawSlot(
|
||||
MatrixStack matrices, int mouseX, int mouseY, float delta, CallbackInfo ci,
|
||||
int i, int j, int k, Slot slot) {
|
||||
SlotRenderEvents.Before event = new SlotRenderEvents.Before(matrices, slot, mouseX, mouseY, delta);
|
||||
SlotRenderEvents.Before.Companion.publish(event);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,11 @@
|
||||
package moe.nea.firmament.mixins;
|
||||
|
||||
import net.minecraft.entity.player.PlayerInventory;
|
||||
import org.spongepowered.asm.mixin.Mixin;
|
||||
|
||||
@Mixin(PlayerInventory.class)
|
||||
public class MixinPlayerInventory {
|
||||
|
||||
|
||||
|
||||
}
|
||||
@@ -5,6 +5,7 @@ import io.ktor.client.*
|
||||
import io.ktor.client.plugins.*
|
||||
import io.ktor.client.plugins.contentnegotiation.*
|
||||
import io.ktor.serialization.kotlinx.json.*
|
||||
import java.awt.Taskbar.Feature
|
||||
import java.nio.file.Files
|
||||
import java.nio.file.Path
|
||||
import net.fabricmc.api.ClientModInitializer
|
||||
@@ -70,7 +71,9 @@ object Firmament : ModInitializer, ClientModInitializer {
|
||||
}
|
||||
|
||||
override fun onInitialize() {
|
||||
}
|
||||
|
||||
override fun onInitializeClient() {
|
||||
dbusConnection.requestBusName("moe.nea.firmament")
|
||||
dbusConnection.exportObject(FirmamentDbusObject)
|
||||
IDataHolder.registerEvents()
|
||||
@@ -85,8 +88,6 @@ object Firmament : ModInitializer, ClientModInitializer {
|
||||
globalJob.cancel()
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
override fun onInitializeClient() {
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,10 @@
|
||||
package moe.nea.firmament.events
|
||||
|
||||
import net.minecraft.client.option.KeyBinding
|
||||
|
||||
data class HandledScreenKeyPressedEvent(val keyCode: Int, val scanCode: Int, val modifiers: Int) : FirmamentEvent.Cancellable() {
|
||||
companion object : FirmamentEventBus<HandledScreenKeyPressedEvent>()
|
||||
fun matches(keyBinding: KeyBinding): Boolean {
|
||||
return keyBinding.matchesKey(keyCode, scanCode)
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,26 @@
|
||||
package moe.nea.firmament.events
|
||||
|
||||
import net.minecraft.screen.slot.Slot
|
||||
import net.minecraft.text.Text
|
||||
import moe.nea.firmament.util.CommonSoundEffects
|
||||
import moe.nea.firmament.util.MC
|
||||
|
||||
data class IsSlotProtectedEvent(
|
||||
val slot: Slot, var isProtected: Boolean = false
|
||||
) : FirmamentEvent() {
|
||||
companion object : FirmamentEventBus<IsSlotProtectedEvent>() {
|
||||
@JvmStatic
|
||||
fun shouldBlockInteraction(slot: Slot): Boolean {
|
||||
return publish(IsSlotProtectedEvent(slot)).isProtected.also {
|
||||
if (it) {
|
||||
MC.player?.sendMessage(Text.translatable("firmament.protectitem").append(slot.stack.name))
|
||||
CommonSoundEffects.playFailure()
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fun protect() {
|
||||
isProtected = true
|
||||
}
|
||||
}
|
||||
32
src/main/kotlin/moe/nea/firmament/events/SlotRenderEvents.kt
Normal file
32
src/main/kotlin/moe/nea/firmament/events/SlotRenderEvents.kt
Normal file
@@ -0,0 +1,32 @@
|
||||
package moe.nea.firmament.events
|
||||
|
||||
import net.minecraft.client.util.math.MatrixStack
|
||||
import net.minecraft.screen.slot.Slot
|
||||
|
||||
interface SlotRenderEvents {
|
||||
val matrices: MatrixStack
|
||||
val slot: Slot
|
||||
val mouseX: Int
|
||||
val mouseY: Int
|
||||
val delta: Float
|
||||
|
||||
data class Before(
|
||||
override val matrices: MatrixStack, override val slot: Slot,
|
||||
override val mouseX: Int,
|
||||
override val mouseY: Int,
|
||||
override val delta: Float
|
||||
) : FirmamentEvent(),
|
||||
SlotRenderEvents {
|
||||
companion object : FirmamentEventBus<Before>()
|
||||
}
|
||||
|
||||
data class After(
|
||||
override val matrices: MatrixStack, override val slot: Slot,
|
||||
override val mouseX: Int,
|
||||
override val mouseY: Int,
|
||||
override val delta: Float
|
||||
) : FirmamentEvent(),
|
||||
SlotRenderEvents {
|
||||
companion object : FirmamentEventBus<After>()
|
||||
}
|
||||
}
|
||||
@@ -4,6 +4,7 @@ import kotlinx.serialization.Serializable
|
||||
import kotlinx.serialization.serializer
|
||||
import moe.nea.firmament.Firmament
|
||||
import moe.nea.firmament.features.fishing.FishingWarning
|
||||
import moe.nea.firmament.features.inventory.SlotLocking
|
||||
import moe.nea.firmament.features.world.FairySouls
|
||||
import moe.nea.firmament.util.data.DataHolder
|
||||
|
||||
@@ -13,7 +14,7 @@ object FeatureManager : DataHolder<FeatureManager.Config>(serializer(), "feature
|
||||
val enabledFeatures: MutableMap<String, Boolean> = mutableMapOf()
|
||||
)
|
||||
|
||||
private val features = mutableMapOf<String, NEUFeature>()
|
||||
private val features = mutableMapOf<String, FirmamentFeature>()
|
||||
|
||||
private var hasAutoloaded = false
|
||||
|
||||
@@ -26,11 +27,12 @@ object FeatureManager : DataHolder<FeatureManager.Config>(serializer(), "feature
|
||||
if (hasAutoloaded) return
|
||||
loadFeature(FairySouls)
|
||||
loadFeature(FishingWarning)
|
||||
loadFeature(SlotLocking)
|
||||
hasAutoloaded = true
|
||||
}
|
||||
}
|
||||
|
||||
fun loadFeature(feature: NEUFeature) {
|
||||
fun loadFeature(feature: FirmamentFeature) {
|
||||
synchronized(features) {
|
||||
if (feature.identifier in features) {
|
||||
Firmament.logger.error("Double registering feature ${feature.identifier}. Ignoring second instance $feature")
|
||||
|
||||
@@ -2,7 +2,7 @@ package moe.nea.firmament.features
|
||||
|
||||
import moe.nea.firmament.util.config.ManagedConfig
|
||||
|
||||
interface NEUFeature {
|
||||
interface FirmamentFeature {
|
||||
val name: String
|
||||
val identifier: String
|
||||
val defaultEnabled: Boolean
|
||||
@@ -13,13 +13,13 @@ import net.minecraft.util.math.Vec3d
|
||||
import moe.nea.firmament.events.ParticleSpawnEvent
|
||||
import moe.nea.firmament.events.WorldReadyEvent
|
||||
import moe.nea.firmament.events.WorldRenderLastEvent
|
||||
import moe.nea.firmament.features.NEUFeature
|
||||
import moe.nea.firmament.features.FirmamentFeature
|
||||
import moe.nea.firmament.util.MC
|
||||
import moe.nea.firmament.util.TimeMark
|
||||
import moe.nea.firmament.util.config.ManagedConfig
|
||||
import moe.nea.firmament.util.render.RenderBlockContext.Companion.renderBlocks
|
||||
|
||||
object FishingWarning : NEUFeature {
|
||||
object FishingWarning : FirmamentFeature {
|
||||
override val name: String
|
||||
get() = "Fishing Warning"
|
||||
override val identifier: String
|
||||
|
||||
@@ -0,0 +1,68 @@
|
||||
package moe.nea.firmament.features.inventory
|
||||
|
||||
import kotlinx.serialization.Serializable
|
||||
import kotlinx.serialization.serializer
|
||||
import net.minecraft.client.gui.DrawableHelper
|
||||
import net.minecraft.entity.player.PlayerInventory
|
||||
import moe.nea.firmament.events.HandledScreenKeyPressedEvent
|
||||
import moe.nea.firmament.events.IsSlotProtectedEvent
|
||||
import moe.nea.firmament.events.SlotRenderEvents
|
||||
import moe.nea.firmament.features.FirmamentFeature
|
||||
import moe.nea.firmament.keybindings.FirmamentKeyBindings
|
||||
import moe.nea.firmament.mixins.accessor.AccessorHandledScreen
|
||||
import moe.nea.firmament.util.CommonSoundEffects
|
||||
import moe.nea.firmament.util.MC
|
||||
import moe.nea.firmament.util.data.ProfileSpecificDataHolder
|
||||
|
||||
object SlotLocking : FirmamentFeature {
|
||||
override val name: String
|
||||
get() = "Slot Locking"
|
||||
override val identifier: String
|
||||
get() = "slot-locking"
|
||||
|
||||
@Serializable
|
||||
data class Data(
|
||||
val lockedSlots: MutableSet<Int> = mutableSetOf(),
|
||||
)
|
||||
|
||||
object DConfig : ProfileSpecificDataHolder<Data>(serializer(), "locked-slots", ::Data)
|
||||
|
||||
val keyBinding by FirmamentKeyBindings::SLOT_LOCKING
|
||||
val lockedSlots get() = DConfig.data?.lockedSlots
|
||||
override fun onLoad() {
|
||||
HandledScreenKeyPressedEvent.subscribe {
|
||||
if (!it.matches(keyBinding)) return@subscribe
|
||||
val inventory = MC.handledScreen ?: return@subscribe
|
||||
inventory as AccessorHandledScreen
|
||||
|
||||
val slot = inventory.focusedSlot_NEU ?: return@subscribe
|
||||
val lockedSlots = lockedSlots ?: return@subscribe
|
||||
if (slot.inventory is PlayerInventory) {
|
||||
if (slot.index in lockedSlots) {
|
||||
lockedSlots.remove(slot.index)
|
||||
} else {
|
||||
lockedSlots.add(slot.index)
|
||||
}
|
||||
DConfig.markDirty()
|
||||
CommonSoundEffects.playSuccess()
|
||||
}
|
||||
}
|
||||
IsSlotProtectedEvent.subscribe {
|
||||
if (it.slot.inventory is PlayerInventory && it.slot.index in (lockedSlots ?: setOf())) {
|
||||
it.protect()
|
||||
}
|
||||
}
|
||||
SlotRenderEvents.Before.subscribe {
|
||||
if (it.slot.inventory is PlayerInventory && it.slot.index in (lockedSlots ?: setOf())) {
|
||||
DrawableHelper.fill(
|
||||
it.matrices,
|
||||
it.slot.x,
|
||||
it.slot.y,
|
||||
it.slot.x + 16,
|
||||
it.slot.y + 16,
|
||||
0xFFFF0000.toInt()
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -6,7 +6,7 @@ import kotlinx.serialization.serializer
|
||||
import moe.nea.firmament.events.ServerChatLineReceivedEvent
|
||||
import moe.nea.firmament.events.SkyblockServerUpdateEvent
|
||||
import moe.nea.firmament.events.WorldRenderLastEvent
|
||||
import moe.nea.firmament.features.NEUFeature
|
||||
import moe.nea.firmament.features.FirmamentFeature
|
||||
import moe.nea.firmament.repo.RepoManager
|
||||
import moe.nea.firmament.util.MC
|
||||
import moe.nea.firmament.util.SBData
|
||||
@@ -17,7 +17,7 @@ import moe.nea.firmament.util.render.RenderBlockContext.Companion.renderBlocks
|
||||
import moe.nea.firmament.util.unformattedString
|
||||
|
||||
|
||||
object FairySouls : NEUFeature {
|
||||
object FairySouls : FirmamentFeature {
|
||||
|
||||
|
||||
@Serializable
|
||||
|
||||
@@ -0,0 +1,17 @@
|
||||
package moe.nea.firmament.keybindings
|
||||
|
||||
import net.fabricmc.fabric.api.client.keybinding.v1.KeyBindingHelper
|
||||
import org.lwjgl.glfw.GLFW
|
||||
import net.minecraft.client.option.KeyBinding
|
||||
import net.minecraft.client.util.InputUtil
|
||||
|
||||
object FirmamentKeyBindings {
|
||||
val SLOT_LOCKING = KeyBindingHelper.registerKeyBinding(
|
||||
KeyBinding(
|
||||
"firmament.key.slotlocking",
|
||||
InputUtil.Type.KEYSYM,
|
||||
GLFW.GLFW_KEY_L,
|
||||
"firmament.key.category"
|
||||
)
|
||||
)
|
||||
}
|
||||
28
src/main/kotlin/moe/nea/firmament/util/CommonSoundEffects.kt
Normal file
28
src/main/kotlin/moe/nea/firmament/util/CommonSoundEffects.kt
Normal file
@@ -0,0 +1,28 @@
|
||||
package moe.nea.firmament.util
|
||||
|
||||
import net.minecraft.client.sound.AbstractSoundInstance
|
||||
import net.minecraft.client.sound.PositionedSoundInstance
|
||||
import net.minecraft.client.sound.SoundInstance
|
||||
import net.minecraft.sound.SoundCategory
|
||||
import net.minecraft.sound.SoundEvent
|
||||
import net.minecraft.util.Identifier
|
||||
|
||||
// TODO: Replace these with custom sound events that just re use the vanilla ogg s
|
||||
object CommonSoundEffects {
|
||||
fun playSound(identifier: Identifier) {
|
||||
MC.soundManager.play(PositionedSoundInstance.master(SoundEvent.of(identifier), 1F))
|
||||
}
|
||||
|
||||
fun playFailure() {
|
||||
playSound(Identifier("minecraft", "block.anvil.place"))
|
||||
}
|
||||
|
||||
|
||||
fun playSuccess() {
|
||||
playDing()
|
||||
}
|
||||
|
||||
fun playDing() {
|
||||
playSound(Identifier("minecraft", "entity.arrow.hit_player"))
|
||||
}
|
||||
}
|
||||
@@ -2,11 +2,16 @@ package moe.nea.firmament.util
|
||||
|
||||
import io.github.moulberry.repo.data.Coordinate
|
||||
import net.minecraft.client.MinecraftClient
|
||||
import net.minecraft.client.gui.screen.ingame.HandledScreen
|
||||
import net.minecraft.util.math.BlockPos
|
||||
import moe.nea.firmament.mixins.accessor.AccessorHandledScreen
|
||||
|
||||
object MC {
|
||||
inline val soundManager get() = MinecraftClient.getInstance().soundManager
|
||||
inline val player get() = MinecraftClient.getInstance().player
|
||||
inline val world get() = MinecraftClient.getInstance().world
|
||||
inline val screen get() = MinecraftClient.getInstance().currentScreen
|
||||
inline val handledScreen: HandledScreen<*>? get() = MinecraftClient.getInstance().currentScreen as? HandledScreen<*>
|
||||
}
|
||||
|
||||
val Coordinate.blockPos: BlockPos
|
||||
|
||||
@@ -22,5 +22,8 @@
|
||||
"firmament.config.fairy-souls.show": "Show Fairy Soul Waypoints",
|
||||
"firmament.config.fairy-souls.reset": "Reset Collected Fairy Souls",
|
||||
"firmament.config.fishing-warning.display-warning": "Display a warning when you are about to hook a fish",
|
||||
"firmament.config.fishing-warning.highlight-wake-chain": "Highlight fishing particles"
|
||||
"firmament.config.fishing-warning.highlight-wake-chain": "Highlight fishing particles",
|
||||
"firmament.key.slotlocking": "Lock Slot / Slot Binding",
|
||||
"firmament.key.category": "Firmament",
|
||||
"firmament.protectitem": "Firmament protected your item: "
|
||||
}
|
||||
|
||||
@@ -40,8 +40,9 @@
|
||||
"firmament.mixins.json"
|
||||
],
|
||||
"depends": {
|
||||
"fabric": "*",
|
||||
"fabric-language-kotlin": ">=${fabric_kotlin_version}",
|
||||
"minecraft": ">=${minecraft_version}"
|
||||
"fabric": "*",
|
||||
"fabric-language-kotlin": ">=${fabric_kotlin_version}",
|
||||
"minecraft": ">=${minecraft_version}",
|
||||
"fabric-key-binding-api-v1": "*"
|
||||
}
|
||||
}
|
||||
|
||||
@@ -13,6 +13,9 @@
|
||||
],
|
||||
"mixins": [
|
||||
"MixinClientPacketHandler",
|
||||
"MixinClientPlayerEntity",
|
||||
"MixinHandledScreen",
|
||||
"MixinPlayerInventory",
|
||||
"devenv.DisableInvalidFishingHook",
|
||||
"devenv.MixinScoreboard"
|
||||
],
|
||||
|
||||
Reference in New Issue
Block a user