diff options
19 files changed, 297 insertions, 14 deletions
@@ -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 diff --git a/src/main/java/moe/nea/firmament/mixins/MixinClientPlayerEntity.java b/src/main/java/moe/nea/firmament/mixins/MixinClientPlayerEntity.java new file mode 100644 index 0000000..dc19786 --- /dev/null +++ b/src/main/java/moe/nea/firmament/mixins/MixinClientPlayerEntity.java @@ -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); + } + } +} diff --git a/src/main/java/moe/nea/firmament/mixins/MixinHandledScreen.java b/src/main/java/moe/nea/firmament/mixins/MixinHandledScreen.java new file mode 100644 index 0000000..00a4176 --- /dev/null +++ b/src/main/java/moe/nea/firmament/mixins/MixinHandledScreen.java @@ -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); + } +} diff --git a/src/main/java/moe/nea/firmament/mixins/MixinPlayerInventory.java b/src/main/java/moe/nea/firmament/mixins/MixinPlayerInventory.java new file mode 100644 index 0000000..dcccf08 --- /dev/null +++ b/src/main/java/moe/nea/firmament/mixins/MixinPlayerInventory.java @@ -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 { + + + +} diff --git a/src/main/kotlin/moe/nea/firmament/Firmament.kt b/src/main/kotlin/moe/nea/firmament/Firmament.kt index ec6cd3e..a375b2d 100644 --- a/src/main/kotlin/moe/nea/firmament/Firmament.kt +++ b/src/main/kotlin/moe/nea/firmament/Firmament.kt @@ -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() { } } diff --git a/src/main/kotlin/moe/nea/firmament/events/HandledScreenKeyPressedEvent.kt b/src/main/kotlin/moe/nea/firmament/events/HandledScreenKeyPressedEvent.kt new file mode 100644 index 0000000..1afbfe5 --- /dev/null +++ b/src/main/kotlin/moe/nea/firmament/events/HandledScreenKeyPressedEvent.kt @@ -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) + } +} diff --git a/src/main/kotlin/moe/nea/firmament/events/IsSlotProtectedEvent.kt b/src/main/kotlin/moe/nea/firmament/events/IsSlotProtectedEvent.kt new file mode 100644 index 0000000..7ca6521 --- /dev/null +++ b/src/main/kotlin/moe/nea/firmament/events/IsSlotProtectedEvent.kt @@ -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 + } +} diff --git a/src/main/kotlin/moe/nea/firmament/events/SlotRenderEvents.kt b/src/main/kotlin/moe/nea/firmament/events/SlotRenderEvents.kt new file mode 100644 index 0000000..afcddf2 --- /dev/null +++ b/src/main/kotlin/moe/nea/firmament/events/SlotRenderEvents.kt @@ -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>() + } +} diff --git a/src/main/kotlin/moe/nea/firmament/features/FeatureManager.kt b/src/main/kotlin/moe/nea/firmament/features/FeatureManager.kt index 68205f4..580d745 100644 --- a/src/main/kotlin/moe/nea/firmament/features/FeatureManager.kt +++ b/src/main/kotlin/moe/nea/firmament/features/FeatureManager.kt @@ -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") diff --git a/src/main/kotlin/moe/nea/firmament/features/NEUFeature.kt b/src/main/kotlin/moe/nea/firmament/features/FirmamentFeature.kt index f231003..c9cb8f0 100644 --- a/src/main/kotlin/moe/nea/firmament/features/NEUFeature.kt +++ b/src/main/kotlin/moe/nea/firmament/features/FirmamentFeature.kt @@ -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 diff --git a/src/main/kotlin/moe/nea/firmament/features/fishing/FishingWarning.kt b/src/main/kotlin/moe/nea/firmament/features/fishing/FishingWarning.kt index cdeb24c..ab8ebf4 100644 --- a/src/main/kotlin/moe/nea/firmament/features/fishing/FishingWarning.kt +++ b/src/main/kotlin/moe/nea/firmament/features/fishing/FishingWarning.kt @@ -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 diff --git a/src/main/kotlin/moe/nea/firmament/features/inventory/SlotLocking.kt b/src/main/kotlin/moe/nea/firmament/features/inventory/SlotLocking.kt new file mode 100644 index 0000000..9f75541 --- /dev/null +++ b/src/main/kotlin/moe/nea/firmament/features/inventory/SlotLocking.kt @@ -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() + ) + } + } + } +} diff --git a/src/main/kotlin/moe/nea/firmament/features/world/FairySouls.kt b/src/main/kotlin/moe/nea/firmament/features/world/FairySouls.kt index f752963..ffde0b9 100644 --- a/src/main/kotlin/moe/nea/firmament/features/world/FairySouls.kt +++ b/src/main/kotlin/moe/nea/firmament/features/world/FairySouls.kt @@ -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 diff --git a/src/main/kotlin/moe/nea/firmament/keybindings/FirmamentKeyBindings.kt b/src/main/kotlin/moe/nea/firmament/keybindings/FirmamentKeyBindings.kt new file mode 100644 index 0000000..0f945d5 --- /dev/null +++ b/src/main/kotlin/moe/nea/firmament/keybindings/FirmamentKeyBindings.kt @@ -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" + ) + ) +} diff --git a/src/main/kotlin/moe/nea/firmament/util/CommonSoundEffects.kt b/src/main/kotlin/moe/nea/firmament/util/CommonSoundEffects.kt new file mode 100644 index 0000000..819ea27 --- /dev/null +++ b/src/main/kotlin/moe/nea/firmament/util/CommonSoundEffects.kt @@ -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")) + } +} diff --git a/src/main/kotlin/moe/nea/firmament/util/MC.kt b/src/main/kotlin/moe/nea/firmament/util/MC.kt index ab35f0e..8fc15b5 100644 --- a/src/main/kotlin/moe/nea/firmament/util/MC.kt +++ b/src/main/kotlin/moe/nea/firmament/util/MC.kt @@ -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 diff --git a/src/main/resources/assets/firmament/lang/en_us.json b/src/main/resources/assets/firmament/lang/en_us.json index bb0a0d0..ca50bb1 100644 --- a/src/main/resources/assets/firmament/lang/en_us.json +++ b/src/main/resources/assets/firmament/lang/en_us.json @@ -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: " } diff --git a/src/main/resources/fabric.mod.json b/src/main/resources/fabric.mod.json index 7d33cb9..c08adb9 100644 --- a/src/main/resources/fabric.mod.json +++ b/src/main/resources/fabric.mod.json @@ -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": "*" } } diff --git a/src/main/resources/firmament.mixins.json b/src/main/resources/firmament.mixins.json index ac040cb..aa35abf 100644 --- a/src/main/resources/firmament.mixins.json +++ b/src/main/resources/firmament.mixins.json @@ -13,6 +13,9 @@ ], "mixins": [ "MixinClientPacketHandler", + "MixinClientPlayerEntity", + "MixinHandledScreen", + "MixinPlayerInventory", "devenv.DisableInvalidFishingHook", "devenv.MixinScoreboard" ], |