feat: Allow dropping protected items in dungeons

This commit is contained in:
Linnea Gräf
2025-01-17 18:00:43 +01:00
parent 19bc576b67
commit 74a043e535
9 changed files with 158 additions and 86 deletions

View File

@@ -4,8 +4,11 @@ package moe.nea.firmament.mixins;
import com.llamalad7.mixinextras.injector.wrapoperation.Operation; import com.llamalad7.mixinextras.injector.wrapoperation.Operation;
import com.llamalad7.mixinextras.injector.wrapoperation.WrapOperation; import com.llamalad7.mixinextras.injector.wrapoperation.WrapOperation;
import com.llamalad7.mixinextras.sugar.Local; import moe.nea.firmament.events.HandledScreenClickEvent;
import moe.nea.firmament.events.*; import moe.nea.firmament.events.HandledScreenForegroundEvent;
import moe.nea.firmament.events.HandledScreenKeyPressedEvent;
import moe.nea.firmament.events.IsSlotProtectedEvent;
import moe.nea.firmament.events.SlotRenderEvents;
import net.minecraft.client.gui.DrawContext; import net.minecraft.client.gui.DrawContext;
import net.minecraft.client.gui.screen.ingame.HandledScreen; import net.minecraft.client.gui.screen.ingame.HandledScreen;
import net.minecraft.entity.player.PlayerInventory; import net.minecraft.entity.player.PlayerInventory;
@@ -22,9 +25,6 @@ import org.spongepowered.asm.mixin.injection.At;
import org.spongepowered.asm.mixin.injection.Inject; import org.spongepowered.asm.mixin.injection.Inject;
import org.spongepowered.asm.mixin.injection.callback.CallbackInfo; import org.spongepowered.asm.mixin.injection.callback.CallbackInfo;
import org.spongepowered.asm.mixin.injection.callback.CallbackInfoReturnable; import org.spongepowered.asm.mixin.injection.callback.CallbackInfoReturnable;
import org.spongepowered.asm.mixin.injection.callback.LocalCapture;
import java.util.Iterator;
@Mixin(value = HandledScreen.class, priority = 990) @Mixin(value = HandledScreen.class, priority = 990)
public abstract class MixinHandledScreen<T extends ScreenHandler> { public abstract class MixinHandledScreen<T extends ScreenHandler> {
@@ -74,17 +74,17 @@ public abstract class MixinHandledScreen<T extends ScreenHandler> {
public void onMouseClickedSlot(Slot slot, int slotId, int button, SlotActionType actionType, CallbackInfo ci) { public void onMouseClickedSlot(Slot slot, int slotId, int button, SlotActionType actionType, CallbackInfo ci) {
if (slotId == -999 && getScreenHandler() != null && actionType == SlotActionType.PICKUP) { // -999 is code for "clicked outside the main window" if (slotId == -999 && getScreenHandler() != null && actionType == SlotActionType.PICKUP) { // -999 is code for "clicked outside the main window"
ItemStack cursorStack = getScreenHandler().getCursorStack(); ItemStack cursorStack = getScreenHandler().getCursorStack();
if (cursorStack != null && IsSlotProtectedEvent.shouldBlockInteraction(slot, SlotActionType.THROW, cursorStack)) { if (cursorStack != null && IsSlotProtectedEvent.shouldBlockInteraction(slot, SlotActionType.THROW, IsSlotProtectedEvent.MoveOrigin.INVENTORY_MOVE, cursorStack)) {
ci.cancel(); ci.cancel();
return; return;
} }
} }
if (IsSlotProtectedEvent.shouldBlockInteraction(slot, actionType)) { if (IsSlotProtectedEvent.shouldBlockInteraction(slot, actionType, IsSlotProtectedEvent.MoveOrigin.INVENTORY_MOVE)) {
ci.cancel(); ci.cancel();
return; return;
} }
if (actionType == SlotActionType.SWAP && 0 <= button && button < 9) { if (actionType == SlotActionType.SWAP && 0 <= button && button < 9) {
if (IsSlotProtectedEvent.shouldBlockInteraction(new Slot(playerInventory, button, 0, 0), actionType)) { if (IsSlotProtectedEvent.shouldBlockInteraction(new Slot(playerInventory, button, 0, 0), actionType, IsSlotProtectedEvent.MoveOrigin.INVENTORY_MOVE)) {
ci.cancel(); ci.cancel();
} }
} }

View File

@@ -21,7 +21,7 @@ public abstract class PlayerDropEventPatch extends PlayerEntity {
@Inject(method = "dropSelectedItem", at = @At("HEAD"), cancellable = true) @Inject(method = "dropSelectedItem", at = @At("HEAD"), cancellable = true)
public void onDropSelectedItem(boolean entireStack, CallbackInfoReturnable<Boolean> cir) { public void onDropSelectedItem(boolean entireStack, CallbackInfoReturnable<Boolean> cir) {
Slot fakeSlot = new Slot(getInventory(), getInventory().selectedSlot, 0, 0); Slot fakeSlot = new Slot(getInventory(), getInventory().selectedSlot, 0, 0);
if (IsSlotProtectedEvent.shouldBlockInteraction(fakeSlot, SlotActionType.THROW)) { if (IsSlotProtectedEvent.shouldBlockInteraction(fakeSlot, SlotActionType.THROW, IsSlotProtectedEvent.MoveOrigin.DROP_FROM_HOTBAR)) {
cir.setReturnValue(false); cir.setReturnValue(false);
} }
} }

View File

@@ -1,5 +1,3 @@
package moe.nea.firmament.events package moe.nea.firmament.events
import net.minecraft.item.ItemStack import net.minecraft.item.ItemStack
@@ -14,12 +12,14 @@ data class IsSlotProtectedEvent(
val actionType: SlotActionType, val actionType: SlotActionType,
var isProtected: Boolean, var isProtected: Boolean,
val itemStackOverride: ItemStack?, val itemStackOverride: ItemStack?,
val origin: MoveOrigin,
var silent: Boolean = false, var silent: Boolean = false,
) : FirmamentEvent() { ) : FirmamentEvent() {
val itemStack get() = itemStackOverride ?: slot!!.stack val itemStack get() = itemStackOverride ?: slot!!.stack
fun protect() { fun protect() {
isProtected = true isProtected = true
silent = false
} }
fun protectSilent() { fun protectSilent() {
@@ -29,12 +29,22 @@ data class IsSlotProtectedEvent(
isProtected = true isProtected = true
} }
enum class MoveOrigin {
DROP_FROM_HOTBAR,
SALVAGE,
INVENTORY_MOVE
;
}
companion object : FirmamentEventBus<IsSlotProtectedEvent>() { companion object : FirmamentEventBus<IsSlotProtectedEvent>() {
@JvmStatic @JvmStatic
@JvmOverloads @JvmOverloads
fun shouldBlockInteraction(slot: Slot?, action: SlotActionType, itemStackOverride: ItemStack? = null): Boolean { fun shouldBlockInteraction(
slot: Slot?, action: SlotActionType,
origin: MoveOrigin,
itemStackOverride: ItemStack? = null,
): Boolean {
if (slot == null && itemStackOverride == null) return false if (slot == null && itemStackOverride == null) return false
val event = IsSlotProtectedEvent(slot, action, false, itemStackOverride) val event = IsSlotProtectedEvent(slot, action, false, itemStackOverride, origin)
publish(event) publish(event)
if (event.isProtected && !event.silent) { if (event.isProtected && !event.silent) {
MC.sendChat(Text.translatable("firmament.protectitem").append(event.itemStack.name)) MC.sendChat(Text.translatable("firmament.protectitem").append(event.itemStack.name))

View File

@@ -45,10 +45,6 @@ object FeatureManager : DataHolder<FeatureManager.Config>(serializer(), "feature
private var hasAutoloaded = false private var hasAutoloaded = false
init {
autoload()
}
fun autoload() { fun autoload() {
synchronized(this) { synchronized(this) {
if (hasAutoloaded) return if (hasAutoloaded) return

View File

@@ -15,6 +15,7 @@ import net.minecraft.screen.slot.SlotActionType
import net.minecraft.util.Identifier import net.minecraft.util.Identifier
import net.minecraft.util.StringIdentifiable import net.minecraft.util.StringIdentifiable
import moe.nea.firmament.annotations.Subscribe import moe.nea.firmament.annotations.Subscribe
import moe.nea.firmament.events.FeaturesInitializedEvent
import moe.nea.firmament.events.HandledScreenForegroundEvent import moe.nea.firmament.events.HandledScreenForegroundEvent
import moe.nea.firmament.events.HandledScreenKeyPressedEvent import moe.nea.firmament.events.HandledScreenKeyPressedEvent
import moe.nea.firmament.events.HandledScreenKeyReleasedEvent import moe.nea.firmament.events.HandledScreenKeyReleasedEvent
@@ -37,6 +38,7 @@ import moe.nea.firmament.util.mc.displayNameAccordingToNbt
import moe.nea.firmament.util.mc.loreAccordingToNbt import moe.nea.firmament.util.mc.loreAccordingToNbt
import moe.nea.firmament.util.render.GuiRenderLayers import moe.nea.firmament.util.render.GuiRenderLayers
import moe.nea.firmament.util.render.drawLine import moe.nea.firmament.util.render.drawLine
import moe.nea.firmament.util.skyblock.DungeonUtil
import moe.nea.firmament.util.skyblockUUID import moe.nea.firmament.util.skyblockUUID
import moe.nea.firmament.util.unformattedString import moe.nea.firmament.util.unformattedString
@@ -60,6 +62,7 @@ object SlotLocking : FirmamentFeature {
val slotBind by keyBinding("bind") { GLFW.GLFW_KEY_L } val slotBind by keyBinding("bind") { GLFW.GLFW_KEY_L }
val slotBindRequireShift by toggle("require-quick-move") { true } val slotBindRequireShift by toggle("require-quick-move") { true }
val slotRenderLines by choice("bind-render") { SlotRenderLinesMode.ONLY_BOXES } val slotRenderLines by choice("bind-render") { SlotRenderLinesMode.ONLY_BOXES }
val allowDroppingInDungeons by toggle("drop-in-dungeons") { true }
} }
enum class SlotRenderLinesMode : StringIdentifiable { enum class SlotRenderLinesMode : StringIdentifiable {
@@ -120,7 +123,11 @@ object SlotLocking : FirmamentFeature {
var anyBlocked = false var anyBlocked = false
for (i in 0 until event.slot.index) { for (i in 0 until event.slot.index) {
val stack = inv.getStack(i) val stack = inv.getStack(i)
if (IsSlotProtectedEvent.shouldBlockInteraction(null, SlotActionType.THROW, stack)) if (IsSlotProtectedEvent.shouldBlockInteraction(null,
SlotActionType.THROW,
IsSlotProtectedEvent.MoveOrigin.SALVAGE,
stack)
)
anyBlocked = true anyBlocked = true
} }
if (anyBlocked) { if (anyBlocked) {
@@ -155,6 +162,19 @@ object SlotLocking : FirmamentFeature {
} }
} }
@Subscribe
fun onEvent(event: FeaturesInitializedEvent) {
IsSlotProtectedEvent.subscribe(receivesCancelled = true, "SlotLocking:unlockInDungeons") {
if (it.isProtected
&& it.origin == IsSlotProtectedEvent.MoveOrigin.DROP_FROM_HOTBAR
&& DungeonUtil.isInActiveDungeon
&& TConfig.allowDroppingInDungeons
) {
it.isProtected = false
}
}
}
@Subscribe @Subscribe
fun onQuickMoveBoundSlot(it: IsSlotProtectedEvent) { fun onQuickMoveBoundSlot(it: IsSlotProtectedEvent) {
val boundSlots = DConfig.data?.boundSlots ?: mapOf() val boundSlots = DConfig.data?.boundSlots ?: mapOf()

View File

@@ -1,8 +1,6 @@
package moe.nea.firmament.util package moe.nea.firmament.util
import java.util.* import java.util.Optional
import net.minecraft.client.gui.hud.InGameHud import net.minecraft.client.gui.hud.InGameHud
import net.minecraft.scoreboard.ScoreboardDisplaySlot import net.minecraft.scoreboard.ScoreboardDisplaySlot
import net.minecraft.scoreboard.Team import net.minecraft.scoreboard.Team
@@ -10,8 +8,20 @@ import net.minecraft.text.StringVisitable
import net.minecraft.text.Style import net.minecraft.text.Style
import net.minecraft.text.Text import net.minecraft.text.Text
import net.minecraft.util.Formatting import net.minecraft.util.Formatting
import moe.nea.firmament.annotations.Subscribe
import moe.nea.firmament.events.TickEvent
fun getScoreboardLines(): List<Text> { object ScoreboardUtil {
var scoreboardLines: List<Text> = listOf()
var simplifiedScoreboardLines: List<String> = listOf()
@Subscribe
fun onTick(event: TickEvent) {
scoreboardLines = getScoreboardLinesUncached()
simplifiedScoreboardLines = scoreboardLines.map { it.unformattedString }
}
private fun getScoreboardLinesUncached(): List<Text> {
val scoreboard = MC.player?.scoreboard ?: return listOf() val scoreboard = MC.player?.scoreboard ?: return listOf()
val activeObjective = scoreboard.getObjectiveForSlot(ScoreboardDisplaySlot.SIDEBAR) ?: return listOf() val activeObjective = scoreboard.getObjectiveForSlot(ScoreboardDisplaySlot.SIDEBAR) ?: return listOf()
return scoreboard.getScoreboardEntries(activeObjective) return scoreboard.getScoreboardEntries(activeObjective)
@@ -23,7 +33,7 @@ fun getScoreboardLines(): List<Text> {
Team.decorateName(team, text) Team.decorateName(team, text)
} }
} }
}
fun Text.formattedString(): String { fun Text.formattedString(): String {
val sb = StringBuilder() val sb = StringBuilder()

View File

@@ -36,6 +36,7 @@ private constructor(
val RIFT = forMode("rift") val RIFT = forMode("rift")
val MINESHAFT = forMode("mineshaft") val MINESHAFT = forMode("mineshaft")
val GARDEN = forMode("garden") val GARDEN = forMode("garden")
val DUNGEON = forMode("dungeon")
} }
val userFriendlyName val userFriendlyName

View File

@@ -0,0 +1,33 @@
package moe.nea.firmament.util.skyblock
import moe.nea.firmament.util.SBData
import moe.nea.firmament.util.ScoreboardUtil
import moe.nea.firmament.util.SkyBlockIsland
import moe.nea.firmament.util.TIME_PATTERN
object DungeonUtil {
val isInDungeonIsland get() = SBData.skyblockLocation == SkyBlockIsland.DUNGEON
private val timeElapsedRegex = "Time Elapsed: $TIME_PATTERN".toRegex()
val isInActiveDungeon get() = isInDungeonIsland && ScoreboardUtil.simplifiedScoreboardLines.any { it.matches(
timeElapsedRegex) }
/*Title:
§f§lSKYBLOCK§B§L CO-OP
' Late Spring 7th'
' §75:20am'
' §7⏣ §cThe Catacombs §7(M3)'
' §7♲ §7Ironman'
' '
'Keys: §c■ §c✗ §8■ §a1x'
'Time Elapsed: §a46s'
'Cleared: §660% §8(105)'
' '
'§e[B] §b151_Dragon §e2,062§c❤'
'§e[A] §6Lennart0312 §a17,165§c'
'§e[T] §b187i §a14,581§c❤'
'§e[H] §bFlameeke §a8,998§c❤'
' '
'§ewww.hypixel.net'*/
}

View File

@@ -231,6 +231,8 @@
"firmament.config.slot-locking.bind-render.choice.only_boxes": "Only boxes", "firmament.config.slot-locking.bind-render.choice.only_boxes": "Only boxes",
"firmament.config.slot-locking.bind-render.description": "Disable rendering of the slot binding lines (or all of the slot binding rendering), unless the relevant slot is being hovered.", "firmament.config.slot-locking.bind-render.description": "Disable rendering of the slot binding lines (or all of the slot binding rendering), unless the relevant slot is being hovered.",
"firmament.config.slot-locking.bind.description": "Bind a hotbar slot to another slot. This allows quick switching between the slots by shift clicking on either slot.", "firmament.config.slot-locking.bind.description": "Bind a hotbar slot to another slot. This allows quick switching between the slots by shift clicking on either slot.",
"firmament.config.slot-locking.drop-in-dungeons": "Allow Dungeon Abilities",
"firmament.config.slot-locking.drop-in-dungeons.description": "Allow dropping items in dungeons, to use your dungeon ultimate abilities.",
"firmament.config.slot-locking.lock": "Lock Slot", "firmament.config.slot-locking.lock": "Lock Slot",
"firmament.config.slot-locking.lock-uuid": "Lock UUID (Lock Item)", "firmament.config.slot-locking.lock-uuid": "Lock UUID (Lock Item)",
"firmament.config.slot-locking.lock-uuid.description": "Lock a SkyBlock item by it's UUID. This blocks a specific item from being dropped/sold, but still allows moving it around.", "firmament.config.slot-locking.lock-uuid.description": "Lock a SkyBlock item by it's UUID. This blocks a specific item from being dropped/sold, but still allows moving it around.",