feat: Allow dropping protected items in dungeons
This commit is contained in:
@@ -4,8 +4,11 @@ package moe.nea.firmament.mixins;
|
||||
|
||||
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;
|
||||
@@ -22,9 +25,6 @@ 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;
|
||||
|
||||
import java.util.Iterator;
|
||||
|
||||
@Mixin(value = HandledScreen.class, priority = 990)
|
||||
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) {
|
||||
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();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -14,15 +14,15 @@ import org.spongepowered.asm.mixin.injection.callback.CallbackInfoReturnable;
|
||||
|
||||
@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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,5 +1,3 @@
|
||||
|
||||
|
||||
package moe.nea.firmament.events
|
||||
|
||||
import net.minecraft.item.ItemStack
|
||||
@@ -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
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -45,10 +45,6 @@ object FeatureManager : DataHolder<FeatureManager.Config>(serializer(), "feature
|
||||
|
||||
private var hasAutoloaded = false
|
||||
|
||||
init {
|
||||
autoload()
|
||||
}
|
||||
|
||||
fun autoload() {
|
||||
synchronized(this) {
|
||||
if (hasAutoloaded) return
|
||||
|
||||
@@ -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
|
||||
@@ -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
|
||||
|
||||
@@ -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 {
|
||||
@@ -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) {
|
||||
@@ -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()
|
||||
@@ -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)
|
||||
|
||||
@@ -1,8 +1,6 @@
|
||||
|
||||
|
||||
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
|
||||
@@ -10,36 +8,48 @@ 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(), "")
|
||||
}
|
||||
|
||||
@@ -36,6 +36,7 @@ private constructor(
|
||||
val RIFT = forMode("rift")
|
||||
val MINESHAFT = forMode("mineshaft")
|
||||
val GARDEN = forMode("garden")
|
||||
val DUNGEON = forMode("dungeon")
|
||||
}
|
||||
|
||||
val userFriendlyName
|
||||
|
||||
33
src/main/kotlin/util/skyblock/DungeonUtil.kt
Normal file
33
src/main/kotlin/util/skyblock/DungeonUtil.kt
Normal 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'*/
|
||||
}
|
||||
@@ -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.",
|
||||
|
||||
Reference in New Issue
Block a user