Skip to content

Commit

Permalink
feat: Allow dropping protected items in dungeons
Browse files Browse the repository at this point in the history
  • Loading branch information
nea89o committed Jan 17, 2025
1 parent 19bc576 commit 74a043e
Show file tree
Hide file tree
Showing 9 changed files with 158 additions and 86 deletions.
16 changes: 8 additions & 8 deletions src/main/java/moe/nea/firmament/mixins/MixinHandledScreen.java
Original file line number Diff line number Diff line change
Expand Up @@ -4,8 +4,11 @@

import com.llamalad7.mixinextras.injector.wrapoperation.Operation;
import com.llamalad7.mixinextras.injector.wrapoperation.WrapOperation;
import com.llamalad7.mixinextras.sugar.Local;
import moe.nea.firmament.events.*;
import moe.nea.firmament.events.HandledScreenClickEvent;
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.screen.ingame.HandledScreen;
import net.minecraft.entity.player.PlayerInventory;
Expand All @@ -22,9 +25,6 @@
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;

import java.util.Iterator;

@Mixin(value = HandledScreen.class, priority = 990)
public abstract class MixinHandledScreen<T extends ScreenHandler> {
Expand Down Expand Up @@ -74,17 +74,17 @@ public void onAfterRenderForeground(DrawContext context, int mouseX, int mouseY,
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"
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();
return;
}
}
if (IsSlotProtectedEvent.shouldBlockInteraction(slot, actionType)) {
if (IsSlotProtectedEvent.shouldBlockInteraction(slot, actionType, IsSlotProtectedEvent.MoveOrigin.INVENTORY_MOVE)) {
ci.cancel();
return;
}
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();
}
}
Expand Down
20 changes: 10 additions & 10 deletions src/main/java/moe/nea/firmament/mixins/PlayerDropEventPatch.java
Original file line number Diff line number Diff line change
Expand Up @@ -14,15 +14,15 @@

@Mixin(ClientPlayerEntity.class)
public abstract class PlayerDropEventPatch extends PlayerEntity {
public PlayerDropEventPatch() {
super(null, null, 0, null);
}
public PlayerDropEventPatch() {
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, SlotActionType.THROW)) {
cir.setReturnValue(false);
}
}
@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, SlotActionType.THROW, IsSlotProtectedEvent.MoveOrigin.DROP_FROM_HOTBAR)) {
cir.setReturnValue(false);
}
}
}
72 changes: 41 additions & 31 deletions src/main/kotlin/events/IsSlotProtectedEvent.kt
Original file line number Diff line number Diff line change
@@ -1,5 +1,3 @@


package moe.nea.firmament.events

import net.minecraft.item.ItemStack
Expand All @@ -10,37 +8,49 @@ import moe.nea.firmament.util.CommonSoundEffects
import moe.nea.firmament.util.MC

data class IsSlotProtectedEvent(
val slot: Slot?,
val actionType: SlotActionType,
var isProtected: Boolean,
val itemStackOverride: ItemStack?,
var silent: Boolean = false,
val slot: Slot?,
val actionType: SlotActionType,
var isProtected: Boolean,
val itemStackOverride: ItemStack?,
val origin: MoveOrigin,
var silent: Boolean = false,
) : FirmamentEvent() {
val itemStack get() = itemStackOverride ?: slot!!.stack
val itemStack get() = itemStackOverride ?: slot!!.stack

fun protect() {
isProtected = true
}
fun protect() {
isProtected = true
silent = false
}

fun protectSilent() {
if (!isProtected) {
silent = true
}
isProtected = true
}
fun protectSilent() {
if (!isProtected) {
silent = true
}
isProtected = true
}

companion object : FirmamentEventBus<IsSlotProtectedEvent>() {
@JvmStatic
@JvmOverloads
fun shouldBlockInteraction(slot: Slot?, action: SlotActionType, itemStackOverride: ItemStack? = null): Boolean {
if (slot == null && itemStackOverride == null) return false
val event = IsSlotProtectedEvent(slot, action, false, itemStackOverride)
publish(event)
if (event.isProtected && !event.silent) {
MC.sendChat(Text.translatable("firmament.protectitem").append(event.itemStack.name))
CommonSoundEffects.playFailure()
}
return event.isProtected
}
}
enum class MoveOrigin {
DROP_FROM_HOTBAR,
SALVAGE,
INVENTORY_MOVE
;
}
companion object : FirmamentEventBus<IsSlotProtectedEvent>() {
@JvmStatic
@JvmOverloads
fun shouldBlockInteraction(
slot: Slot?, action: SlotActionType,
origin: MoveOrigin,
itemStackOverride: ItemStack? = null,
): Boolean {
if (slot == null && itemStackOverride == null) return false
val event = IsSlotProtectedEvent(slot, action, false, itemStackOverride, origin)
publish(event)
if (event.isProtected && !event.silent) {
MC.sendChat(Text.translatable("firmament.protectitem").append(event.itemStack.name))
CommonSoundEffects.playFailure()
}
return event.isProtected
}
}
}
4 changes: 0 additions & 4 deletions src/main/kotlin/features/FeatureManager.kt
Original file line number Diff line number Diff line change
Expand Up @@ -45,10 +45,6 @@ object FeatureManager : DataHolder<FeatureManager.Config>(serializer(), "feature

private var hasAutoloaded = false

init {
autoload()
}

fun autoload() {
synchronized(this) {
if (hasAutoloaded) return
Expand Down
24 changes: 22 additions & 2 deletions src/main/kotlin/features/inventory/SlotLocking.kt
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@ import net.minecraft.screen.slot.SlotActionType
import net.minecraft.util.Identifier
import net.minecraft.util.StringIdentifiable
import moe.nea.firmament.annotations.Subscribe
import moe.nea.firmament.events.FeaturesInitializedEvent
import moe.nea.firmament.events.HandledScreenForegroundEvent
import moe.nea.firmament.events.HandledScreenKeyPressedEvent
import moe.nea.firmament.events.HandledScreenKeyReleasedEvent
Expand All @@ -37,6 +38,7 @@ import moe.nea.firmament.util.mc.displayNameAccordingToNbt
import moe.nea.firmament.util.mc.loreAccordingToNbt
import moe.nea.firmament.util.render.GuiRenderLayers
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.unformattedString

Expand All @@ -60,6 +62,7 @@ object SlotLocking : FirmamentFeature {
val slotBind by keyBinding("bind") { GLFW.GLFW_KEY_L }
val slotBindRequireShift by toggle("require-quick-move") { true }
val slotRenderLines by choice("bind-render") { SlotRenderLinesMode.ONLY_BOXES }
val allowDroppingInDungeons by toggle("drop-in-dungeons") { true }
}

enum class SlotRenderLinesMode : StringIdentifiable {
Expand Down Expand Up @@ -120,7 +123,11 @@ object SlotLocking : FirmamentFeature {
var anyBlocked = false
for (i in 0 until event.slot.index) {
val stack = inv.getStack(i)
if (IsSlotProtectedEvent.shouldBlockInteraction(null, SlotActionType.THROW, stack))
if (IsSlotProtectedEvent.shouldBlockInteraction(null,
SlotActionType.THROW,
IsSlotProtectedEvent.MoveOrigin.SALVAGE,
stack)
)
anyBlocked = true
}
if (anyBlocked) {
Expand Down Expand Up @@ -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
fun onQuickMoveBoundSlot(it: IsSlotProtectedEvent) {
val boundSlots = DConfig.data?.boundSlots ?: mapOf()
Expand Down Expand Up @@ -245,7 +265,7 @@ object SlotLocking : FirmamentFeature {
val (hotX, hotY) = hotbarSlot.lineCenter()
val (invX, invY) = inventorySlot.lineCenter()
val anyHovered = accScreen.focusedSlot_Firmament === hotbarSlot
|| accScreen.focusedSlot_Firmament === inventorySlot
|| accScreen.focusedSlot_Firmament === inventorySlot
if (!anyHovered && TConfig.slotRenderLines == SlotRenderLinesMode.NOTHING)
continue
val color = if (anyHovered)
Expand Down
72 changes: 41 additions & 31 deletions src/main/kotlin/util/ScoreboardUtil.kt
Original file line number Diff line number Diff line change
@@ -1,45 +1,55 @@


package moe.nea.firmament.util

import java.util.*
import java.util.Optional
import net.minecraft.client.gui.hud.InGameHud
import net.minecraft.scoreboard.ScoreboardDisplaySlot
import net.minecraft.scoreboard.Team
import net.minecraft.text.StringVisitable
import net.minecraft.text.Style
import net.minecraft.text.Text
import net.minecraft.util.Formatting
import moe.nea.firmament.annotations.Subscribe
import moe.nea.firmament.events.TickEvent

fun getScoreboardLines(): List<Text> {
val scoreboard = MC.player?.scoreboard ?: return listOf()
val activeObjective = scoreboard.getObjectiveForSlot(ScoreboardDisplaySlot.SIDEBAR) ?: return listOf()
return scoreboard.getScoreboardEntries(activeObjective)
.filter { !it.hidden() }
.sortedWith(InGameHud.SCOREBOARD_ENTRY_COMPARATOR)
.take(15).map {
val team = scoreboard.getScoreHolderTeam(it.owner)
val text = it.name()
Team.decorateName(team, 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 activeObjective = scoreboard.getObjectiveForSlot(ScoreboardDisplaySlot.SIDEBAR) ?: return listOf()
return scoreboard.getScoreboardEntries(activeObjective)
.filter { !it.hidden() }
.sortedWith(InGameHud.SCOREBOARD_ENTRY_COMPARATOR)
.take(15).map {
val team = scoreboard.getScoreHolderTeam(it.owner)
val text = it.name()
Team.decorateName(team, text)
}
}
}

fun Text.formattedString(): String {
val sb = StringBuilder()
visit(StringVisitable.StyledVisitor<Unit> { style, string ->
val c = Formatting.byName(style.color?.name)
if (c != null) {
sb.append("§${c.code}")
}
if (style.isUnderlined) {
sb.append("§n")
}
if (style.isBold) {
sb.append("§l")
}
sb.append(string)
Optional.empty()
}, Style.EMPTY)
return sb.toString().replace("§[^a-f0-9]".toRegex(), "")
val sb = StringBuilder()
visit(StringVisitable.StyledVisitor<Unit> { style, string ->
val c = Formatting.byName(style.color?.name)
if (c != null) {
sb.append("§${c.code}")
}
if (style.isUnderlined) {
sb.append("§n")
}
if (style.isBold) {
sb.append("§l")
}
sb.append(string)
Optional.empty()
}, Style.EMPTY)
return sb.toString().replace("§[^a-f0-9]".toRegex(), "")
}
1 change: 1 addition & 0 deletions src/main/kotlin/util/SkyBlockIsland.kt
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,7 @@ private constructor(
val RIFT = forMode("rift")
val MINESHAFT = forMode("mineshaft")
val GARDEN = forMode("garden")
val DUNGEON = forMode("dungeon")
}

val userFriendlyName
Expand Down
33 changes: 33 additions & 0 deletions src/main/kotlin/util/skyblock/DungeonUtil.kt
Original file line number Diff line number Diff line change
@@ -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'*/
}
2 changes: 2 additions & 0 deletions translations/en_us.json
Original file line number Diff line number Diff line change
Expand Up @@ -231,6 +231,8 @@
"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.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-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.",
Expand Down

0 comments on commit 74a043e

Please sign in to comment.