Inventory filters preview
This commit is contained in:
parent
3354c698c4
commit
30f07e2fed
@ -0,0 +1,49 @@
|
||||
package ru.dbotthepony.mc.otm.mixin;
|
||||
|
||||
import net.minecraft.CrashReport;
|
||||
import net.minecraft.CrashReportCategory;
|
||||
import net.minecraft.ReportedException;
|
||||
import net.minecraft.world.entity.player.Inventory;
|
||||
import net.minecraft.world.entity.player.Player;
|
||||
import net.minecraft.world.item.Item;
|
||||
import net.minecraft.world.item.ItemStack;
|
||||
import net.minecraftforge.registries.ForgeRegistries;
|
||||
import org.spongepowered.asm.mixin.Mixin;
|
||||
import org.spongepowered.asm.mixin.Shadow;
|
||||
import org.spongepowered.asm.mixin.injection.At;
|
||||
import org.spongepowered.asm.mixin.injection.Inject;
|
||||
import org.spongepowered.asm.mixin.injection.callback.CallbackInfoReturnable;
|
||||
import ru.dbotthepony.mc.otm.capability.MatteryCapability;
|
||||
|
||||
@Mixin(Inventory.class)
|
||||
public class MixinInventory {
|
||||
@Shadow Player player;
|
||||
|
||||
@Inject(
|
||||
method = "add(ILnet/minecraft/world/item/ItemStack;)Z",
|
||||
at = @At("HEAD"),
|
||||
cancellable = true
|
||||
)
|
||||
private void add(int pSlot, ItemStack pStack, CallbackInfoReturnable<Boolean> hook) {
|
||||
if (pStack.isEmpty()) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (pSlot == -1) {
|
||||
this.player.getCapability(MatteryCapability.MATTERY_PLAYER).ifPresent(it -> {
|
||||
try {
|
||||
hook.setReturnValue(it.inventoryAddImpl(pStack));
|
||||
} catch (Throwable throwable) {
|
||||
CrashReport crashreport = CrashReport.forThrowable(throwable, "Adding item to inventory (Overdrive That Matters detour)");
|
||||
CrashReportCategory crashreportcategory = crashreport.addCategory("Item being added");
|
||||
crashreportcategory.setDetail("Registry Name", () -> String.valueOf(ForgeRegistries.ITEMS.getKey(pStack.getItem())));
|
||||
crashreportcategory.setDetail("Item Class", () -> pStack.getItem().getClass().getName());
|
||||
crashreportcategory.setDetail("Item ID", Item.getId(pStack.getItem()));
|
||||
crashreportcategory.setDetail("Item data", pStack.getDamageValue());
|
||||
crashreportcategory.setDetail("Item name", () -> pStack.getHoverName().getString());
|
||||
throw new ReportedException(crashreport);
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
@ -1,10 +1,12 @@
|
||||
package ru.dbotthepony.mc.otm.capability
|
||||
|
||||
import it.unimi.dsi.fastutil.ints.Int2ObjectOpenHashMap
|
||||
import it.unimi.dsi.fastutil.objects.Reference2ObjectOpenHashMap
|
||||
import net.minecraft.ChatFormatting
|
||||
import net.minecraft.core.Direction
|
||||
import net.minecraft.nbt.CompoundTag
|
||||
import net.minecraft.nbt.ListTag
|
||||
import net.minecraft.nbt.NumericTag
|
||||
import net.minecraft.nbt.StringTag
|
||||
import net.minecraft.network.chat.Component
|
||||
import net.minecraft.resources.ResourceLocation
|
||||
@ -18,7 +20,9 @@ import net.minecraft.world.entity.boss.wither.WitherBoss
|
||||
import net.minecraft.world.entity.monster.Phantom
|
||||
import net.minecraft.world.entity.player.Inventory
|
||||
import net.minecraft.world.entity.player.Player
|
||||
import net.minecraft.world.item.Item
|
||||
import net.minecraft.world.item.ItemStack
|
||||
import net.minecraft.world.item.Items
|
||||
import net.minecraft.world.item.ProjectileWeaponItem
|
||||
import net.minecraft.world.item.enchantment.EnchantmentHelper.hasVanishingCurse
|
||||
import net.minecraft.world.level.Level
|
||||
@ -58,11 +62,15 @@ import ru.dbotthepony.mc.otm.core.collect.nonEmpty
|
||||
import ru.dbotthepony.mc.otm.core.math.Decimal
|
||||
import ru.dbotthepony.mc.otm.core.math.minus
|
||||
import ru.dbotthepony.mc.otm.core.nbt.getCompoundList
|
||||
import ru.dbotthepony.mc.otm.core.nbt.getStringList
|
||||
import ru.dbotthepony.mc.otm.core.nbt.map
|
||||
import ru.dbotthepony.mc.otm.core.nbt.set
|
||||
import ru.dbotthepony.mc.otm.core.util.IntValueCodec
|
||||
import ru.dbotthepony.mc.otm.core.util.ItemStackValueCodec
|
||||
import ru.dbotthepony.mc.otm.core.util.ItemValueCodec
|
||||
import ru.dbotthepony.mc.otm.core.util.Savetables
|
||||
import ru.dbotthepony.mc.otm.core.util.UUIDValueCodec
|
||||
import ru.dbotthepony.mc.otm.core.util.VarIntValueCodec
|
||||
import ru.dbotthepony.mc.otm.menu.ExoPackInventoryMenu
|
||||
import ru.dbotthepony.mc.otm.network.*
|
||||
import ru.dbotthepony.mc.otm.registry.AndroidFeatures
|
||||
@ -108,6 +116,19 @@ class MatteryPlayerCapability(val ply: Player) : ICapabilityProvider, INBTSerial
|
||||
val level: Level get() = capability.ply.level
|
||||
}
|
||||
|
||||
/**
|
||||
* This event is fired on main event bus when [Inventory.add] was called and entire [ItemStack] can not be stored
|
||||
* (both in [Inventory] and [MatteryPlayerCapability] exopack due to inventory filters or no free space).
|
||||
*/
|
||||
data class ItemStackLeftoverEvent(val stack: ItemStack, val capability: MatteryPlayerCapability) : Event() {
|
||||
override fun isCancelable() = false
|
||||
override fun hasResult() = false
|
||||
override fun setResult(value: Result) {}
|
||||
|
||||
val player get() = capability.ply
|
||||
val level: Level get() = capability.ply.level
|
||||
}
|
||||
|
||||
private inner class PlayerMatteryContainer(size: Int) : MatteryContainer(size) {
|
||||
override fun setChanged(slot: Int, new: ItemStack, old: ItemStack) {
|
||||
super.setChanged(slot, new, old)
|
||||
@ -181,6 +202,16 @@ class MatteryPlayerCapability(val ply: Player) : ICapabilityProvider, INBTSerial
|
||||
}
|
||||
}, backingMap = this.exoPackSlotModifierMap)
|
||||
|
||||
val regularSlotFilters = immutableList(Inventory.INVENTORY_SIZE) {
|
||||
synchronizer.Field(null, ItemValueCodec.nullable)
|
||||
}
|
||||
|
||||
val exoPackSlotFilters by synchronizer.Map(
|
||||
keyCodec = VarIntValueCodec,
|
||||
valueCodec = ItemValueCodec,
|
||||
backingMap = Int2ObjectOpenHashMap()
|
||||
)
|
||||
|
||||
/**
|
||||
* Current slot count of Exopack
|
||||
*
|
||||
@ -291,15 +322,6 @@ class MatteryPlayerCapability(val ply: Player) : ICapabilityProvider, INBTSerial
|
||||
private var wasInLiquid = false
|
||||
private var lastDimension = ResourceLocation("overworld")
|
||||
|
||||
init {
|
||||
savetables.int(::ticksIExist)
|
||||
savetables.int(::iteration)
|
||||
savetables.bool(::shouldSendIteration)
|
||||
savetables.bool(::wasInLiquid)
|
||||
savetables.vector(::lastOutsideLiquid)
|
||||
savetables.location(::lastDimension)
|
||||
}
|
||||
|
||||
/**
|
||||
* Whenever player should become an Android once transformation conditions are met (e.g. player dies or sleeps in bed)
|
||||
*/
|
||||
@ -324,6 +346,26 @@ class MatteryPlayerCapability(val ply: Player) : ICapabilityProvider, INBTSerial
|
||||
*/
|
||||
val androidEnergy = AndroidPowerSource(ply, synchronizer, AndroidConfig.ANDROID_MAX_ENERGY, AndroidConfig.ANDROID_MAX_ENERGY)
|
||||
|
||||
init {
|
||||
savetables.int(::ticksIExist)
|
||||
savetables.int(::iteration)
|
||||
|
||||
savetables.bool(::shouldSendIteration)
|
||||
savetables.bool(::wasInLiquid)
|
||||
savetables.bool(::isAndroid)
|
||||
savetables.bool(::willBecomeAndroid)
|
||||
savetables.bool(::hasExoPack, "hasExoSuit")
|
||||
savetables.bool(::displayExoPack, "displayExoSuit")
|
||||
savetables.bool(::isExoPackCraftingUpgraded, "isExoSuitCraftingUpgraded")
|
||||
|
||||
savetables.vector(::lastOutsideLiquid)
|
||||
savetables.location(::lastDimension)
|
||||
|
||||
savetables.stateful(::exoPackSlotModifier, "exoSuitSlotCountModifiers")
|
||||
savetables.stateful(::exoPackContainer, "exoSuitContainer")
|
||||
savetables.stateful(::androidEnergy)
|
||||
}
|
||||
|
||||
fun invalidateNetworkState() {
|
||||
synchronizer.invalidate()
|
||||
publicSynchronizer.invalidate()
|
||||
@ -641,36 +683,36 @@ class MatteryPlayerCapability(val ply: Player) : ICapabilityProvider, INBTSerial
|
||||
}
|
||||
}
|
||||
|
||||
// exosuit
|
||||
tag["hasExoSuit"] = hasExoPack
|
||||
tag["displayExoSuit"] = displayExoPack
|
||||
tag["exoSuitContainer"] = exoPackContainer.serializeNBT()
|
||||
tag["isExoSuitCraftingUpgraded"] = isExoPackCraftingUpgraded
|
||||
tag["exoSuitSlotCountModifiers"] = exoPackSlotModifier.serializeNBT()
|
||||
|
||||
// android
|
||||
tag["androidEnergy"] = androidEnergy.serializeNBT()
|
||||
|
||||
tag["isAndroid"] = isAndroid
|
||||
tag["willBecomeAndroid"] = willBecomeAndroid
|
||||
|
||||
val featureList = ListTag()
|
||||
val researchList = ListTag()
|
||||
|
||||
for (feature in featureMap.values) {
|
||||
featureList.add(feature.serializeNBT().also {
|
||||
it["id"] = feature.type.registryName!!.toString()
|
||||
})
|
||||
tag["features"] = ListTag().also {
|
||||
for (feature in featureMap.values) {
|
||||
it.add(feature.serializeNBT().also {
|
||||
it["id"] = feature.type.registryName!!.toString()
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
for ((type, instance) in research) {
|
||||
researchList.add(instance.serializeNBT().also {
|
||||
it["id"] = type.id.toString()
|
||||
})
|
||||
tag["research"] = ListTag().also {
|
||||
for ((type, instance) in research) {
|
||||
it.add(instance.serializeNBT().also {
|
||||
it["id"] = type.id.toString()
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
tag["features"] = featureList
|
||||
tag["research"] = researchList
|
||||
tag["exoPackSlotFilters"] = ListTag().also {
|
||||
for ((slot, filter) in exoPackSlotFilters) {
|
||||
it.add(CompoundTag().also {
|
||||
it["slot"] = slot
|
||||
it["filter"] = filter.registryName!!.toString()
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
tag["regularSlotFilters"] = ListTag().also {
|
||||
for (filter in regularSlotFilters) {
|
||||
it.add(StringTag.valueOf(filter.value?.registryName?.toString() ?: ""))
|
||||
}
|
||||
}
|
||||
|
||||
return tag
|
||||
}
|
||||
@ -678,6 +720,29 @@ class MatteryPlayerCapability(val ply: Player) : ICapabilityProvider, INBTSerial
|
||||
override fun deserializeNBT(tag: CompoundTag) {
|
||||
savetables.deserializeNBT(tag)
|
||||
|
||||
for (filter in regularSlotFilters) {
|
||||
filter.value = null
|
||||
}
|
||||
|
||||
exoPackSlotFilters.clear()
|
||||
|
||||
for (elem in tag.getCompoundList("exoPackSlotFilters")) {
|
||||
val slot = (elem["slot"] as? NumericTag)?.asInt ?: continue
|
||||
val filter = (elem["filter"] as? StringTag)?.asString ?: continue
|
||||
|
||||
if (slot >= 0) {
|
||||
exoPackSlotFilters[slot] = ForgeRegistries.ITEMS.getValue(ResourceLocation.tryParse(filter) ?: continue) ?: Items.AIR
|
||||
}
|
||||
}
|
||||
|
||||
val regularSlotFilters = tag.getStringList("regularSlotFilters")
|
||||
|
||||
for (i in 0 until regularSlotFilters.size.coerceAtMost(this.regularSlotFilters.size)) {
|
||||
val path = regularSlotFilters[i].asString
|
||||
if (path == "") continue
|
||||
this.regularSlotFilters[i].value = ForgeRegistries.ITEMS.getValue(ResourceLocation.tryParse(path) ?: continue) ?: Items.AIR
|
||||
}
|
||||
|
||||
// iterations
|
||||
deathLog.clear()
|
||||
|
||||
@ -689,20 +754,6 @@ class MatteryPlayerCapability(val ply: Player) : ICapabilityProvider, INBTSerial
|
||||
}
|
||||
}
|
||||
|
||||
// exosuit
|
||||
hasExoPack = tag.getBoolean("hasExoSuit")
|
||||
displayExoPack = tag.getBoolean("displayExoSuit")
|
||||
tag.map("exoSuitSlotCountModifiers", exoPackSlotModifier::deserializeNBT)
|
||||
|
||||
exoPackContainer.deserializeNBT(tag["exoSuitContainer"])
|
||||
isExoPackCraftingUpgraded = tag.getBoolean("isExoSuitCraftingUpgraded")
|
||||
|
||||
// android
|
||||
isAndroid = tag.getBoolean("isAndroid")
|
||||
willBecomeAndroid = tag.getBoolean("willBecomeAndroid")
|
||||
|
||||
tag.map("androidEnergy", androidEnergy::deserializeNBT)
|
||||
|
||||
featureMap.clear()
|
||||
research.clear()
|
||||
|
||||
@ -977,8 +1028,141 @@ class MatteryPlayerCapability(val ply: Player) : ICapabilityProvider, INBTSerial
|
||||
} else LazyOptional.empty()
|
||||
}
|
||||
|
||||
/**
|
||||
* This re-implement [Inventory.add] logic (original method is redirected to this)
|
||||
*/
|
||||
fun inventoryAddImpl(stack: ItemStack): Boolean {
|
||||
val items = ply.inventory.items
|
||||
val exoPackContainer = exoPackContainer
|
||||
|
||||
if (!stack.isStackable) {
|
||||
for (i in items.indices) {
|
||||
if (items[i].isEmpty && (regularSlotFilters[i].value === null || regularSlotFilters[i].value === stack.item)) {
|
||||
items[i] = stack.copy()
|
||||
items[i].popTime = 5
|
||||
stack.count = 0
|
||||
return true
|
||||
}
|
||||
}
|
||||
|
||||
for (i in 0 until exoPackContainer.containerSize) {
|
||||
if (exoPackContainer[i].isEmpty && (exoPackSlotFilters[i] === null || exoPackSlotFilters[i] === stack.item)) {
|
||||
exoPackContainer[i] = stack.copy()
|
||||
exoPackContainer[i].popTime = 5
|
||||
stack.count = 0
|
||||
return true
|
||||
}
|
||||
}
|
||||
|
||||
MinecraftForge.EVENT_BUS.post(ItemStackLeftoverEvent(stack, this))
|
||||
|
||||
if (ply.abilities.instabuild) {
|
||||
stack.count = 0
|
||||
}
|
||||
|
||||
return !stack.isEmpty
|
||||
}
|
||||
|
||||
// двигаем в существующие слоты
|
||||
items[ply.inventory.selected].also { stackStacks(it, stack) }
|
||||
if (stack.isEmpty) return true
|
||||
ply.inventory.offhand[0].also { stackStacks(it, stack) }
|
||||
if (stack.isEmpty) return true
|
||||
|
||||
for (i in items.indices) {
|
||||
if (stackStacks(items[i], stack) && stack.isEmpty) {
|
||||
return true
|
||||
}
|
||||
}
|
||||
|
||||
for (i in 0 until exoPackContainer.containerSize) {
|
||||
if (stackStacks(exoPackContainer[i], stack)) {
|
||||
exoPackContainer.setChanged(i)
|
||||
|
||||
if (stack.isEmpty) {
|
||||
return true
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// двигаем в пустые слоты
|
||||
|
||||
// двигаем в отфильтрованные слоты
|
||||
for (i in items.indices) {
|
||||
if (items[i].isEmpty && regularSlotFilters[i].value === stack.item) {
|
||||
items[i] = stack.split(stack.count.coerceAtMost(stack.maxStackSize))
|
||||
items[i].popTime = 5
|
||||
|
||||
if (stack.isEmpty) {
|
||||
return true
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
for (i in 0 until exoPackContainer.containerSize) {
|
||||
if (exoPackContainer[i].isEmpty && exoPackSlotFilters[i] === stack.item) {
|
||||
exoPackContainer[i] = stack.split(stack.count.coerceAtMost(stack.maxStackSize))
|
||||
exoPackContainer[i].popTime = 5
|
||||
|
||||
if (stack.isEmpty) {
|
||||
return true
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// двигаем в обычные слоты
|
||||
for (i in items.indices) {
|
||||
if (items[i].isEmpty && regularSlotFilters[i].value === null) {
|
||||
items[i] = stack.split(stack.count.coerceAtMost(stack.maxStackSize))
|
||||
items[i].popTime = 5
|
||||
|
||||
if (stack.isEmpty) {
|
||||
return true
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
for (i in 0 until exoPackContainer.containerSize) {
|
||||
if (exoPackContainer[i].isEmpty && exoPackSlotFilters[i] === null) {
|
||||
exoPackContainer[i] = stack.split(stack.count.coerceAtMost(stack.maxStackSize))
|
||||
exoPackContainer[i].popTime = 5
|
||||
|
||||
if (stack.isEmpty) {
|
||||
return true
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
MinecraftForge.EVENT_BUS.post(ItemStackLeftoverEvent(stack, this))
|
||||
|
||||
if (ply.abilities.instabuild) {
|
||||
stack.count = 0
|
||||
}
|
||||
|
||||
return !stack.isEmpty
|
||||
}
|
||||
|
||||
@Suppress("unused")
|
||||
companion object {
|
||||
private fun stackStacks(it: ItemStack, stack: ItemStack): Boolean {
|
||||
if (
|
||||
!it.isEmpty &&
|
||||
it.maxStackSize > it.count &&
|
||||
it.isStackable &&
|
||||
ItemStack.isSameItemSameTags(it, stack)
|
||||
) {
|
||||
val new = (it.count + stack.count).coerceAtMost(it.maxStackSize)
|
||||
val diff = new - it.count
|
||||
it.count = new
|
||||
stack.count -= diff
|
||||
it.popTime = 5
|
||||
|
||||
return true
|
||||
}
|
||||
|
||||
return false
|
||||
}
|
||||
|
||||
init {
|
||||
WitherBoss.TARGETING_CONDITIONS.selector(WitherBoss.TARGETING_CONDITIONS.selector!!.and { it.matteryPlayer?.isAndroid != true })
|
||||
WitherBoss.LIVING_ENTITY_SELECTOR = WitherBoss.LIVING_ENTITY_SELECTOR.and { it.matteryPlayer?.isAndroid != true }
|
||||
|
@ -23,7 +23,7 @@ class AndroidPowerSource(
|
||||
synchronizer: FieldSynchronizer,
|
||||
initialCharge: Decimal,
|
||||
maxCharge: Decimal
|
||||
) : IMatteryEnergyStorage, INBTSerializable<CompoundTag> {
|
||||
) : IMatteryEnergyStorage, INBTSerializable<CompoundTag?> {
|
||||
override val energyFlow: FlowDirection
|
||||
get() = FlowDirection.INPUT
|
||||
|
||||
@ -46,7 +46,8 @@ class AndroidPowerSource(
|
||||
}
|
||||
}
|
||||
|
||||
override fun deserializeNBT(tag: CompoundTag) {
|
||||
override fun deserializeNBT(tag: CompoundTag?) {
|
||||
tag ?: return
|
||||
battery = tag.getDecimal("battery")
|
||||
maxBattery = tag.getDecimal("maxBattery")
|
||||
item = tag.getItemStack("item")
|
||||
|
@ -10,8 +10,8 @@ import ru.dbotthepony.mc.otm.client.render.sprite
|
||||
import ru.dbotthepony.mc.otm.client.screen.panels.*
|
||||
import ru.dbotthepony.mc.otm.client.screen.panels.button.LargeRectangleButtonPanel
|
||||
import ru.dbotthepony.mc.otm.client.screen.panels.slot.AbstractSlotPanel
|
||||
import ru.dbotthepony.mc.otm.client.screen.panels.slot.FilteredSlotPanel
|
||||
import ru.dbotthepony.mc.otm.client.screen.panels.slot.SlotPanel
|
||||
import ru.dbotthepony.mc.otm.client.screen.panels.util.BackgroundPanel
|
||||
import ru.dbotthepony.mc.otm.client.screen.panels.util.DiscreteScrollBarPanel
|
||||
import ru.dbotthepony.mc.otm.client.setMousePos
|
||||
import ru.dbotthepony.mc.otm.client.shouldOpenVanillaInventory
|
||||
@ -48,10 +48,10 @@ class ExoPackInventoryScreen(menu: ExoPackInventoryMenu) : MatteryScreen<ExoPack
|
||||
override fun makeMainFrame(): FramePanel<MatteryScreen<*>> {
|
||||
val frame = FramePanel(this, width = 200f, height = FRAME_BASE_HEIGHT + inventoryRows * AbstractSlotPanel.SIZE, title = this.title)
|
||||
|
||||
val toolbeltLine = EditablePanel(this, frame, height = 18f)
|
||||
toolbeltLine.dock = Dock.BOTTOM
|
||||
val hotbarStrip = EditablePanel(this, frame, height = 18f)
|
||||
hotbarStrip.dock = Dock.BOTTOM
|
||||
|
||||
toolbeltLine.setDockMargin(top = 3f)
|
||||
hotbarStrip.setDockMargin(top = 3f)
|
||||
|
||||
scrollPanel = DiscreteScrollBarPanel(this, null, maxScroll = { integerDivisionDown(menu.playerCombinedInventorySlots.size, 9) },
|
||||
scrollCallback = {
|
||||
@ -83,11 +83,12 @@ class ExoPackInventoryScreen(menu: ExoPackInventoryMenu) : MatteryScreen<ExoPack
|
||||
mainInventoryLine.dock = Dock.BOTTOM
|
||||
|
||||
for (slot in menu.playerHotbarSlots) {
|
||||
val panel = SlotPanel(this, toolbeltLine, slot)
|
||||
panel.dock = Dock.LEFT
|
||||
FilteredSlotPanel.of(this, hotbarStrip, slot, filter = slot.filter!!).also {
|
||||
it.dock = Dock.LEFT
|
||||
}
|
||||
}
|
||||
|
||||
val offhand = SlotPanel(this, toolbeltLine, menu.offhandSlot)
|
||||
val offhand = SlotPanel(this, hotbarStrip, menu.offhandSlot)
|
||||
offhand.dock = Dock.RIGHT
|
||||
|
||||
for (i in scrollPanel.scroll until scrollPanel.scroll + inventoryRows) {
|
||||
|
@ -11,25 +11,31 @@ import net.minecraft.client.renderer.entity.ItemRenderer
|
||||
import net.minecraft.network.chat.Component
|
||||
import net.minecraft.world.entity.player.Inventory
|
||||
import net.minecraft.world.inventory.Slot
|
||||
import net.minecraft.world.item.Item
|
||||
import net.minecraft.world.item.ItemStack
|
||||
import net.minecraftforge.client.event.ContainerScreenEvent.Render.Background
|
||||
import net.minecraftforge.client.event.ContainerScreenEvent.Render.Foreground
|
||||
import net.minecraftforge.common.MinecraftForge
|
||||
import org.lwjgl.opengl.GL11
|
||||
import org.lwjgl.opengl.GL13
|
||||
import ru.dbotthepony.mc.otm.capability.matteryPlayer
|
||||
import ru.dbotthepony.mc.otm.config.ClientConfig
|
||||
import ru.dbotthepony.mc.otm.client.moveMousePosScaled
|
||||
import ru.dbotthepony.mc.otm.client.screen.panels.*
|
||||
import ru.dbotthepony.mc.otm.client.screen.panels.slot.AbstractSlotPanel
|
||||
import ru.dbotthepony.mc.otm.client.screen.panels.slot.FilteredSlotPanel
|
||||
import ru.dbotthepony.mc.otm.client.screen.panels.slot.FoldableSlotPanel
|
||||
import ru.dbotthepony.mc.otm.client.screen.panels.slot.SlotPanel
|
||||
import ru.dbotthepony.mc.otm.client.screen.panels.util.DiscreteScrollBarPanel
|
||||
import ru.dbotthepony.mc.otm.client.screen.panels.util.HeightControls
|
||||
import ru.dbotthepony.mc.otm.client.screen.panels.util.ScrollBarConstants
|
||||
import ru.dbotthepony.mc.otm.compat.cos.CosmeticToggleButton
|
||||
import ru.dbotthepony.mc.otm.core.GetterSetter
|
||||
import ru.dbotthepony.mc.otm.core.math.integerDivisionDown
|
||||
import ru.dbotthepony.mc.otm.menu.MatteryMenu
|
||||
import ru.dbotthepony.mc.otm.menu.PlayerSlot
|
||||
import ru.dbotthepony.mc.otm.network.MatteryPlayerNetworkChannel
|
||||
import ru.dbotthepony.mc.otm.network.SetInventoryFilterPacket
|
||||
import java.util.Collections
|
||||
|
||||
/**
|
||||
@ -163,7 +169,9 @@ abstract class MatteryScreen<T : MatteryMenu>(menu: T, inventory: Inventory, tit
|
||||
hotbarStrip.dock = Dock.BOTTOM
|
||||
|
||||
for (slot in menu.playerHotbarSlots) {
|
||||
SlotPanel(this, hotbarStrip, slot).also { it.dock = Dock.LEFT }
|
||||
FilteredSlotPanel.of(this, hotbarStrip, slot, filter = slot.filter!!).also {
|
||||
it.dock = Dock.LEFT
|
||||
}
|
||||
}
|
||||
|
||||
val canvas = EditablePanel(this, inventoryFrame)
|
||||
@ -211,7 +219,9 @@ abstract class MatteryScreen<T : MatteryMenu>(menu: T, inventory: Inventory, tit
|
||||
hotbarStrip.dock = Dock.BOTTOM
|
||||
|
||||
for (slot in menu.playerHotbarSlots) {
|
||||
SlotPanel(this, hotbarStrip, slot).also { it.dock = Dock.LEFT }
|
||||
FilteredSlotPanel.of(this, hotbarStrip, slot, filter = slot.filter!!).also {
|
||||
it.dock = Dock.LEFT
|
||||
}
|
||||
}
|
||||
|
||||
inventoryScrollbar.parent = slotListCanvas
|
||||
@ -250,13 +260,19 @@ abstract class MatteryScreen<T : MatteryMenu>(menu: T, inventory: Inventory, tit
|
||||
}
|
||||
|
||||
for (i in 0 .. (8).coerceAtMost(menu.playerCombinedInventorySlots.size - offset - 1)) {
|
||||
val slot = object : SlotPanel<MatteryScreen<*>, Slot>(this@MatteryScreen, canvas, menu.playerCombinedInventorySlots[offset + i]) {
|
||||
val slot = menu.playerCombinedInventorySlots[offset + i]
|
||||
|
||||
object : FilteredSlotPanel<MatteryScreen<*>, Slot>(this@MatteryScreen, canvas, slot) {
|
||||
init {
|
||||
dock = Dock.LEFT
|
||||
}
|
||||
|
||||
override fun mouseScrolledInner(x: Double, y: Double, scroll: Double): Boolean {
|
||||
return false
|
||||
}
|
||||
}
|
||||
|
||||
slot.dock = Dock.LEFT
|
||||
override var slotFilter: Item? by slot.filter!!
|
||||
}
|
||||
}
|
||||
|
||||
return@Int2ObjectFunction canvas
|
||||
|
@ -59,7 +59,7 @@ abstract class AbstractSlotPanel<out S : MatteryScreen<*>> @JvmOverloads constru
|
||||
|
||||
if (!itemstack.isEmpty) {
|
||||
// val font = RenderProperties.get(itemstack).getFont(itemstack)
|
||||
val font = (itemstack.item.renderPropertiesInternal as? IClientItemExtensions)?.getFont(itemstack, IClientItemExtensions.FontContext.TOOLTIP)
|
||||
val font = IClientItemExtensions.of(itemstack).getFont(itemstack, IClientItemExtensions.FontContext.TOOLTIP)
|
||||
|
||||
// TODO: WHERE????????????
|
||||
// GuiUtils.preItemToolTip(itemstack);
|
||||
@ -84,5 +84,6 @@ abstract class AbstractSlotPanel<out S : MatteryScreen<*>> @JvmOverloads constru
|
||||
val SLOT_HIGHLIGHT_DRAG = RGBAColor(200, 200, 200, 150)
|
||||
const val SIZE = 18f
|
||||
val SLOT_BACKGROUND = WidgetLocation.MISC.sprite(0f, 0f, SIZE, SIZE)
|
||||
val FILTERED_SLOT_BACKGROUND = WidgetLocation.MISC.sprite(46f, 0f, SIZE, SIZE)
|
||||
}
|
||||
}
|
||||
|
@ -0,0 +1,113 @@
|
||||
package ru.dbotthepony.mc.otm.client.screen.panels.slot
|
||||
|
||||
import com.mojang.blaze3d.platform.InputConstants
|
||||
import com.mojang.blaze3d.vertex.PoseStack
|
||||
import net.minecraft.world.inventory.Slot
|
||||
import net.minecraft.world.item.Item
|
||||
import net.minecraft.world.item.ItemStack
|
||||
import net.minecraft.world.item.Items
|
||||
import net.minecraftforge.client.extensions.common.IClientItemExtensions
|
||||
import ru.dbotthepony.mc.otm.client.isCtrlDown
|
||||
import ru.dbotthepony.mc.otm.client.minecraft
|
||||
import ru.dbotthepony.mc.otm.client.playGuiClickSound
|
||||
import ru.dbotthepony.mc.otm.client.render.MatterySprite
|
||||
import ru.dbotthepony.mc.otm.client.render.drawColor
|
||||
import ru.dbotthepony.mc.otm.client.render.drawRect
|
||||
import ru.dbotthepony.mc.otm.client.screen.MatteryScreen
|
||||
import ru.dbotthepony.mc.otm.client.screen.panels.EditablePanel
|
||||
import ru.dbotthepony.mc.otm.core.GetterSetter
|
||||
import ru.dbotthepony.mc.otm.core.math.RGBAColor
|
||||
|
||||
abstract class FilteredSlotPanel<out S : MatteryScreen<*>, out T : Slot>(
|
||||
screen: S,
|
||||
parent: EditablePanel<*>?,
|
||||
slot: T,
|
||||
x: Float = 0f,
|
||||
y: Float = 0f,
|
||||
width: Float = SIZE,
|
||||
height: Float = SIZE,
|
||||
noItemIcon: MatterySprite? = null
|
||||
) : SlotPanel<S, T>(screen, parent, slot, x, y, width, height, noItemIcon) {
|
||||
abstract var slotFilter: Item?
|
||||
|
||||
override fun renderSlotBackground(stack: PoseStack, mouseX: Float, mouseY: Float, partialTick: Float) {
|
||||
super.renderSlotBackground(stack, mouseX, mouseY, partialTick)
|
||||
|
||||
if (slotFilter != null) {
|
||||
if (slotFilter !== Items.AIR) {
|
||||
val itemStack = ItemStack(slotFilter!!, 1)
|
||||
|
||||
screen.renderItemStack(absoluteX, absoluteY, itemStack, null)
|
||||
clearDepth(stack)
|
||||
}
|
||||
|
||||
drawColor = SLOT_FILTER_COLOR
|
||||
drawRect(stack, 0f, 0f, width, height)
|
||||
}
|
||||
}
|
||||
|
||||
override fun innerRenderTooltips(stack: PoseStack, mouseX: Float, mouseY: Float, partialTick: Float): Boolean {
|
||||
if (isHovered && slotFilter != null && slotFilter !== Items.AIR && itemStack.isEmpty) {
|
||||
val itemstack = ItemStack(slotFilter!!, 1)
|
||||
|
||||
screen.renderComponentTooltip(
|
||||
stack,
|
||||
getItemStackTooltip(itemstack),
|
||||
mouseX.toInt(),
|
||||
mouseY.toInt(),
|
||||
IClientItemExtensions.of(itemstack).getFont(itemstack, IClientItemExtensions.FontContext.TOOLTIP) ?: screen.font,
|
||||
itemstack
|
||||
)
|
||||
}
|
||||
|
||||
return super.innerRenderTooltips(stack, mouseX, mouseY, partialTick)
|
||||
}
|
||||
|
||||
override fun mouseClickedInner(x: Double, y: Double, button: Int): Boolean {
|
||||
if (button == InputConstants.MOUSE_BUTTON_LEFT && minecraft.window.isCtrlDown) {
|
||||
if (slotFilter === null) {
|
||||
if (screen.menu.carried.isEmpty) {
|
||||
slotFilter = slot.item.item
|
||||
} else {
|
||||
slotFilter = screen.menu.carried.item
|
||||
}
|
||||
} else {
|
||||
slotFilter = null
|
||||
}
|
||||
|
||||
playGuiClickSound()
|
||||
|
||||
return true
|
||||
} else {
|
||||
return super.mouseClickedInner(x, y, button)
|
||||
}
|
||||
}
|
||||
|
||||
override fun mouseReleasedInner(x: Double, y: Double, button: Int): Boolean {
|
||||
if (button == InputConstants.MOUSE_BUTTON_LEFT && minecraft.window.isCtrlDown) {
|
||||
return true
|
||||
}
|
||||
|
||||
return super.mouseReleasedInner(x, y, button)
|
||||
}
|
||||
|
||||
companion object {
|
||||
val SLOT_FILTER_COLOR = RGBAColor(85, 113, 216, 150)
|
||||
|
||||
fun <S : MatteryScreen<*>, T : Slot> of(
|
||||
screen: S,
|
||||
parent: EditablePanel<*>?,
|
||||
slot: T,
|
||||
x: Float = 0f,
|
||||
y: Float = 0f,
|
||||
width: Float = SIZE,
|
||||
height: Float = SIZE,
|
||||
noItemIcon: MatterySprite? = null,
|
||||
filter: GetterSetter<Item?>
|
||||
): FilteredSlotPanel<S, T> {
|
||||
return object : FilteredSlotPanel<S, T>(screen, parent, slot, x, y, width, height, noItemIcon) {
|
||||
override var slotFilter: Item? by filter
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
@ -11,6 +11,7 @@ import com.google.gson.JsonObject
|
||||
import com.google.gson.JsonPrimitive
|
||||
import net.minecraft.core.BlockPos
|
||||
import net.minecraft.nbt.CompoundTag
|
||||
import net.minecraft.nbt.NbtAccounter
|
||||
import net.minecraft.network.FriendlyByteBuf
|
||||
import net.minecraft.network.chat.ComponentContents
|
||||
import net.minecraft.network.chat.contents.TranslatableContents
|
||||
@ -18,6 +19,7 @@ import net.minecraft.resources.ResourceLocation
|
||||
import net.minecraft.world.entity.Entity
|
||||
import net.minecraft.world.item.Item
|
||||
import net.minecraft.world.item.ItemStack
|
||||
import net.minecraft.world.item.Items
|
||||
import net.minecraft.world.item.crafting.Ingredient
|
||||
import net.minecraft.world.level.BlockGetter
|
||||
import net.minecraft.world.level.block.state.BlockState
|
||||
@ -32,7 +34,11 @@ import net.minecraftforge.registries.ForgeRegistry
|
||||
import net.minecraftforge.registries.IForgeRegistry
|
||||
import ru.dbotthepony.mc.otm.core.math.Vector
|
||||
import ru.dbotthepony.mc.otm.core.util.readInt
|
||||
import ru.dbotthepony.mc.otm.core.util.readVarIntLE
|
||||
import ru.dbotthepony.mc.otm.core.util.writeInt
|
||||
import ru.dbotthepony.mc.otm.core.util.writeVarIntLE
|
||||
import java.io.InputStream
|
||||
import java.io.OutputStream
|
||||
import java.lang.ref.Reference
|
||||
import java.math.BigInteger
|
||||
import java.util.Arrays
|
||||
@ -167,12 +173,16 @@ fun FriendlyByteBuf.writeItemType(value: Item) {
|
||||
writeInt(ForgeRegistries.ITEMS.getID(value))
|
||||
}
|
||||
|
||||
fun FriendlyByteBuf.readItemType(): Item? {
|
||||
return ForgeRegistries.ITEMS.getValue(readInt())
|
||||
fun OutputStream.writeItemType(value: Item) {
|
||||
writeVarIntLE(ForgeRegistries.ITEMS.getID(value))
|
||||
}
|
||||
|
||||
fun InputStream.readItemType(): Item? {
|
||||
return ForgeRegistries.ITEMS.getValue(readInt())
|
||||
fun FriendlyByteBuf.readItemType(): Item {
|
||||
return ForgeRegistries.ITEMS.getValue(readInt()) ?: Items.AIR
|
||||
}
|
||||
|
||||
fun InputStream.readItemType(sizeLimit: NbtAccounter? = null): Item {
|
||||
return ForgeRegistries.ITEMS.getValue(readVarIntLE(sizeLimit)) ?: Items.AIR
|
||||
}
|
||||
|
||||
operator fun <T : Comparable<T>> StateHolder<*, *>.get(property: Property<T>): T {
|
||||
|
@ -10,6 +10,8 @@ import net.minecraft.world.item.ItemStack
|
||||
import ru.dbotthepony.mc.otm.core.immutableMap
|
||||
import ru.dbotthepony.mc.otm.core.math.readDecimal
|
||||
import ru.dbotthepony.mc.otm.core.math.writeDecimal
|
||||
import ru.dbotthepony.mc.otm.core.readItemType
|
||||
import ru.dbotthepony.mc.otm.core.writeItemType
|
||||
import java.io.DataInput
|
||||
import java.io.DataInputStream
|
||||
import java.io.DataOutput
|
||||
@ -60,6 +62,30 @@ class StreamCodec<V>(
|
||||
comparator: ((a: V, b: V) -> Boolean) = { a, b -> a == b }
|
||||
) : this({ stream, sizeLimit -> sizeLimit.accountBytes(payloadSize); reader.invoke(stream) }, writer, copier, comparator)
|
||||
|
||||
val nullable = object : IStreamCodec<V?> {
|
||||
override fun read(stream: DataInputStream, sizeLimit: NbtAccounter): V? {
|
||||
sizeLimit.accountBytes(1L)
|
||||
return if (stream.read() == 0) null else reader.invoke(stream, sizeLimit)
|
||||
}
|
||||
|
||||
override fun write(stream: DataOutputStream, value: V?) {
|
||||
if (value === null) stream.write(0) else {
|
||||
stream.write(1)
|
||||
writer.invoke(stream, value)
|
||||
}
|
||||
}
|
||||
|
||||
override fun copy(value: V?): V? {
|
||||
return if (value === null) null else copier.invoke(value)
|
||||
}
|
||||
|
||||
override fun compare(a: V?, b: V?): Boolean {
|
||||
if (a === null && b === null) return true
|
||||
if (a === null || b === null) return false
|
||||
return comparator.invoke(a, b)
|
||||
}
|
||||
}
|
||||
|
||||
override fun read(stream: DataInputStream, sizeLimit: NbtAccounter): V {
|
||||
return reader.invoke(stream, sizeLimit)
|
||||
}
|
||||
@ -86,6 +112,7 @@ val LongValueCodec = StreamCodec(DataInputStream::readLong, 8L, DataOutputStream
|
||||
val FloatValueCodec = StreamCodec(DataInputStream::readFloat, 4L, DataOutputStream::writeFloat)
|
||||
val DoubleValueCodec = StreamCodec(DataInputStream::readDouble, 8L, DataOutputStream::writeDouble)
|
||||
val ItemStackValueCodec = StreamCodec(DataInputStream::readItem, DataOutputStream::writeItem, ItemStack::copy) { a, b -> a.equals(b, true) }
|
||||
val ItemValueCodec = StreamCodec(DataInputStream::readItemType, DataOutputStream::writeItemType)
|
||||
val DecimalValueCodec = StreamCodec(DataInputStream::readDecimal, DataOutputStream::writeDecimal)
|
||||
val BigDecimalValueCodec = StreamCodec(DataInputStream::readBigDecimal, DataOutputStream::writeBigDecimal)
|
||||
val UUIDValueCodec = StreamCodec({ s, a -> a.accountBytes(8L); UUID(s.readLong(), s.readLong()) }, { s, v -> s.writeLong(v.mostSignificantBits); s.writeLong(v.leastSignificantBits) })
|
||||
@ -331,7 +358,7 @@ fun OutputStream.writeBinaryString(input: String) {
|
||||
private data class IndexedStreamCodec<T>(
|
||||
val condition: Predicate<Any?>,
|
||||
val id: Int,
|
||||
val codec: StreamCodec<T>
|
||||
val codec: IStreamCodec<T>
|
||||
) {
|
||||
fun read(stream: DataInputStream, sizeLimit: NbtAccounter = NbtAccounter(1L shl 18 /* 256 KiB */)): T {
|
||||
return codec.read(stream, sizeLimit)
|
||||
|
@ -14,6 +14,7 @@ import net.minecraft.world.Container
|
||||
import net.minecraft.world.entity.player.Inventory
|
||||
import net.minecraft.world.entity.player.Player
|
||||
import net.minecraft.world.inventory.*
|
||||
import net.minecraft.world.item.Item
|
||||
import net.minecraft.world.item.ItemStack
|
||||
import net.minecraft.world.item.enchantment.EnchantmentHelper.hasBindingCurse
|
||||
import net.minecraft.world.level.block.entity.BlockEntity
|
||||
@ -26,6 +27,7 @@ import ru.dbotthepony.mc.otm.compat.curios.curiosSlots
|
||||
import ru.dbotthepony.mc.otm.compat.curios.isCurioSlot
|
||||
import ru.dbotthepony.mc.otm.container.ItemFilter
|
||||
import ru.dbotthepony.mc.otm.container.ItemFilterNetworkSlot
|
||||
import ru.dbotthepony.mc.otm.core.GetterSetter
|
||||
import ru.dbotthepony.mc.otm.core.util.BigDecimalValueCodec
|
||||
import ru.dbotthepony.mc.otm.core.util.BinaryStringCodec
|
||||
import ru.dbotthepony.mc.otm.core.util.BooleanValueCodec
|
||||
@ -35,8 +37,10 @@ import ru.dbotthepony.mc.otm.core.util.VarIntValueCodec
|
||||
import ru.dbotthepony.mc.otm.menu.widget.AbstractWidget
|
||||
import ru.dbotthepony.mc.otm.network.FieldSynchronizer
|
||||
import ru.dbotthepony.mc.otm.network.MatteryPacket
|
||||
import ru.dbotthepony.mc.otm.network.MatteryPlayerNetworkChannel
|
||||
import ru.dbotthepony.mc.otm.network.MenuFieldPacket
|
||||
import ru.dbotthepony.mc.otm.network.MenuNetworkChannel
|
||||
import ru.dbotthepony.mc.otm.network.SetInventoryFilterPacket
|
||||
import ru.dbotthepony.mc.otm.network.enqueueWork
|
||||
import ru.dbotthepony.mc.otm.network.packetHandled
|
||||
import ru.dbotthepony.mc.otm.network.sender
|
||||
@ -70,12 +74,12 @@ abstract class MatteryMenu @JvmOverloads protected constructor(
|
||||
private val _matteryWidgets = ArrayList<AbstractWidget>()
|
||||
val matteryWidgets: List<AbstractWidget> = Collections.unmodifiableList(_matteryWidgets)
|
||||
|
||||
private val _playerInventorySlots = ArrayList<MatterySlot>()
|
||||
private val _playerInventorySlots2 = ArrayList<MatterySlot>()
|
||||
private val _playerHotbarSlots = ArrayList<MatterySlot>()
|
||||
private val _playerMainSlots = ArrayList<MatterySlot>()
|
||||
private val _playerExoSuitSlots = ArrayList<MatterySlot>()
|
||||
private val _playerCombinedInventorySlots = ArrayList<MatterySlot>()
|
||||
private val _playerInventorySlots = ArrayList<InventorySlot>()
|
||||
private val _playerInventorySlots2 = ArrayList<InventorySlot>()
|
||||
private val _playerHotbarSlots = ArrayList<InventorySlot>()
|
||||
private val _playerMainSlots = ArrayList<InventorySlot>()
|
||||
private val _playerExoSuitSlots = ArrayList<InventorySlot>()
|
||||
private val _playerCombinedInventorySlots = ArrayList<InventorySlot>()
|
||||
|
||||
private val playerInputs = ArrayList<PlayerInput<*>>()
|
||||
|
||||
@ -155,32 +159,32 @@ abstract class MatteryMenu @JvmOverloads protected constructor(
|
||||
/**
|
||||
* inventory + exosuit + hotbar (in this order)
|
||||
*/
|
||||
val playerInventorySlots: List<MatterySlot> = Collections.unmodifiableList(_playerInventorySlots)
|
||||
val playerInventorySlots: List<InventorySlot> = Collections.unmodifiableList(_playerInventorySlots)
|
||||
|
||||
/**
|
||||
* hotbar + inventory + exosuit (in this order)
|
||||
*/
|
||||
val playerInventorySlots2: List<MatterySlot> = Collections.unmodifiableList(_playerInventorySlots2)
|
||||
val playerInventorySlots2: List<InventorySlot> = Collections.unmodifiableList(_playerInventorySlots2)
|
||||
|
||||
/**
|
||||
* hotbar only
|
||||
*/
|
||||
val playerHotbarSlots: List<MatterySlot> = Collections.unmodifiableList(_playerHotbarSlots)
|
||||
val playerHotbarSlots: List<InventorySlot> = Collections.unmodifiableList(_playerHotbarSlots)
|
||||
|
||||
/**
|
||||
* inventory only
|
||||
*/
|
||||
val playerMainSlots: List<MatterySlot> = Collections.unmodifiableList(_playerMainSlots)
|
||||
val playerMainSlots: List<InventorySlot> = Collections.unmodifiableList(_playerMainSlots)
|
||||
|
||||
/**
|
||||
* exosuit only
|
||||
*/
|
||||
val playerExoSuitSlots: List<MatterySlot> = Collections.unmodifiableList(_playerExoSuitSlots)
|
||||
val playerExoSuitSlots: List<InventorySlot> = Collections.unmodifiableList(_playerExoSuitSlots)
|
||||
|
||||
/**
|
||||
* inventory + exosuit (in this order)
|
||||
*/
|
||||
val playerCombinedInventorySlots: List<MatterySlot> = Collections.unmodifiableList(_playerCombinedInventorySlots)
|
||||
val playerCombinedInventorySlots: List<InventorySlot> = Collections.unmodifiableList(_playerCombinedInventorySlots)
|
||||
|
||||
var autoCreateInventoryFrame = true
|
||||
private set
|
||||
@ -250,6 +254,32 @@ abstract class MatteryMenu @JvmOverloads protected constructor(
|
||||
|
||||
return super.isSameInventory(other)
|
||||
}
|
||||
|
||||
// фильтр существует только для автоматизации
|
||||
// игрок всё равно может класть предмет вручную, даже если он не соответствует фильтру
|
||||
internal val filter: GetterSetter<Item?>?
|
||||
|
||||
init {
|
||||
val mattery = ply.matteryPlayer
|
||||
|
||||
if (mattery != null) {
|
||||
if (container === inventory && slotIndex in mattery.regularSlotFilters.indices) {
|
||||
filter = GetterSetter.of(
|
||||
getter = { mattery.regularSlotFilters[slotIndex].value },
|
||||
setter = { MatteryPlayerNetworkChannel.sendToServer(SetInventoryFilterPacket(SetInventoryFilterPacket.Type.INVENTORY, slotIndex, it)) }
|
||||
)
|
||||
} else if (container === mattery.exoPackContainer) {
|
||||
filter = GetterSetter.of(
|
||||
getter = { mattery.exoPackSlotFilters[slotIndex] },
|
||||
setter = { MatteryPlayerNetworkChannel.sendToServer(SetInventoryFilterPacket(SetInventoryFilterPacket.Type.EXOPACK, slotIndex, it)) }
|
||||
)
|
||||
} else {
|
||||
filter = null
|
||||
}
|
||||
} else {
|
||||
filter = null
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
open inner class EquipmentSlot(container: Container, index: Int, val type: net.minecraft.world.entity.EquipmentSlot, x: Int = 0, y: Int = 0) : InventorySlot(container, index, x, y) {
|
||||
@ -458,6 +488,18 @@ abstract class MatteryMenu @JvmOverloads protected constructor(
|
||||
val copy = slot.item.copy()
|
||||
var any = false
|
||||
|
||||
if (target.any { it.any { it is InventorySlot && it.filter != null } }) {
|
||||
for (collection in target) {
|
||||
if (moveItemStackTo(slot, collection, onlyFiltered = true)) {
|
||||
any = true
|
||||
|
||||
if (!slot.hasItem()) {
|
||||
return copy
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
for (collection in target) {
|
||||
if (moveItemStackTo(slot, collection)) {
|
||||
any = true
|
||||
@ -506,25 +548,12 @@ abstract class MatteryMenu @JvmOverloads protected constructor(
|
||||
return true
|
||||
}
|
||||
|
||||
fun moveItemStackTo(
|
||||
item: ItemStack,
|
||||
slots: Collection<Slot>
|
||||
): Boolean {
|
||||
val remainder = moveItemStackToSlots(item, slots)
|
||||
|
||||
if (remainder.count == item.count) {
|
||||
return false
|
||||
}
|
||||
|
||||
item.count = remainder.count
|
||||
return true
|
||||
}
|
||||
|
||||
fun moveItemStackTo(
|
||||
source: Slot,
|
||||
slots: Collection<Slot>
|
||||
slots: Collection<Slot>,
|
||||
onlyFiltered: Boolean = false
|
||||
): Boolean {
|
||||
val remainder = moveItemStackToSlots(source.item, slots)
|
||||
val remainder = moveItemStackToSlots(source.item, slots, onlyFiltered = onlyFiltered)
|
||||
|
||||
if (remainder.count == source.item.count) {
|
||||
return false
|
||||
@ -540,15 +569,25 @@ abstract class MatteryMenu @JvmOverloads protected constructor(
|
||||
return true
|
||||
}
|
||||
|
||||
fun moveItemStackToSlots(item: ItemStack, slots: Collection<Slot>, simulate: Boolean = false): ItemStack {
|
||||
fun moveItemStackToSlots(item: ItemStack, slots: Collection<Slot>, simulate: Boolean = false, onlyFiltered: Boolean = false): ItemStack {
|
||||
if (item.isEmpty) {
|
||||
return ItemStack.EMPTY
|
||||
}
|
||||
|
||||
val copy = item.copy()
|
||||
|
||||
// first pass - stack with existing slots
|
||||
if (copy.isStackable) {
|
||||
for (slot in slots) {
|
||||
if (onlyFiltered && (slot !is InventorySlot || slot.filter == null || slot.filter.get() != item.item)) {
|
||||
continue
|
||||
} else if (!onlyFiltered && slot is InventorySlot && slot.filter != null && slot.filter.get() != null && slot.filter.get() != item.item) {
|
||||
continue
|
||||
}
|
||||
|
||||
val limit = slot.getMaxStackSize(copy)
|
||||
|
||||
if (limit > slot.item.count && ItemStack.isSameItemSameTags(slot.item, copy) && slot.mayPlace(item)) {
|
||||
if (limit > slot.item.count && slot.mayPlace(item) && ItemStack.isSameItemSameTags(slot.item, copy)) {
|
||||
val newCount = (slot.item.count + copy.count).coerceAtMost(limit)
|
||||
val diff = newCount - slot.item.count
|
||||
copy.count -= diff
|
||||
@ -567,6 +606,12 @@ abstract class MatteryMenu @JvmOverloads protected constructor(
|
||||
|
||||
// second pass - drop stack into first free slot
|
||||
for (slot in slots) {
|
||||
if (onlyFiltered && (slot !is InventorySlot || slot.filter == null || slot.filter.get() != item.item)) {
|
||||
continue
|
||||
} else if (!onlyFiltered && slot is InventorySlot && slot.filter != null && slot.filter.get() != null && slot.filter.get() != item.item) {
|
||||
continue
|
||||
}
|
||||
|
||||
val limit = slot.getMaxStackSize(copy)
|
||||
|
||||
if (!slot.hasItem() && slot.mayPlace(item)) {
|
||||
@ -597,9 +642,19 @@ abstract class MatteryMenu @JvmOverloads protected constructor(
|
||||
require(finalSlot < slots.size) { "Final slot $finalSlot is bigger than total size of array of ${slots.size}" }
|
||||
|
||||
val slots = ArrayList<Slot>(finalSlot - initialSlot + 1)
|
||||
var filters = false
|
||||
|
||||
for (i in (if (inverse) finalSlot downTo initialSlot else initialSlot .. finalSlot)) {
|
||||
slots.add(slots[i])
|
||||
val slot = slots[i]
|
||||
slots.add(slot)
|
||||
|
||||
if (slot is InventorySlot && slot.filter != null) {
|
||||
filters = true
|
||||
}
|
||||
}
|
||||
|
||||
if (filters) {
|
||||
return moveItemStackToSlots(moveItemStackToSlots(item, slots, simulate, onlyFiltered = true), slots, simulate, onlyFiltered = false)
|
||||
}
|
||||
|
||||
return moveItemStackToSlots(item, slots, simulate)
|
||||
|
@ -6,7 +6,9 @@ import net.minecraft.network.chat.Component
|
||||
import net.minecraft.network.protocol.game.ClientboundSetCarriedItemPacket
|
||||
import net.minecraft.resources.ResourceLocation
|
||||
import net.minecraft.world.entity.player.Inventory
|
||||
import net.minecraft.world.item.Item
|
||||
import net.minecraft.world.item.ItemStack
|
||||
import net.minecraft.world.item.Items
|
||||
import net.minecraftforge.network.NetworkDirection.PLAY_TO_CLIENT
|
||||
import net.minecraftforge.network.NetworkDirection.PLAY_TO_SERVER
|
||||
import net.minecraftforge.network.NetworkEvent
|
||||
@ -27,6 +29,8 @@ import ru.dbotthepony.mc.otm.client.render.ShockwaveRenderer
|
||||
import ru.dbotthepony.mc.otm.container.get
|
||||
import ru.dbotthepony.mc.otm.container.set
|
||||
import ru.dbotthepony.mc.otm.core.math.Vector
|
||||
import ru.dbotthepony.mc.otm.core.readItemType
|
||||
import ru.dbotthepony.mc.otm.core.writeItemType
|
||||
import ru.dbotthepony.mc.otm.menu.tech.AndroidStationMenu
|
||||
import ru.dbotthepony.mc.otm.menu.ExoPackInventoryMenu
|
||||
import ru.dbotthepony.mc.otm.registry.AndroidFeatures
|
||||
@ -510,8 +514,56 @@ object HideExosuitPacket : MatteryPacket {
|
||||
}
|
||||
}
|
||||
|
||||
class SetInventoryFilterPacket(val type: Type, val slot: Int, val item: Item?) : MatteryPacket {
|
||||
enum class Type {
|
||||
INVENTORY,
|
||||
EXOPACK
|
||||
}
|
||||
|
||||
override fun write(buff: FriendlyByteBuf) {
|
||||
buff.writeEnum(type)
|
||||
buff.writeVarInt(slot)
|
||||
|
||||
if (item == null) {
|
||||
buff.writeByte(0)
|
||||
} else {
|
||||
buff.writeByte(1)
|
||||
buff.writeItemType(item)
|
||||
}
|
||||
}
|
||||
|
||||
override fun play(context: Supplier<NetworkEvent.Context>) {
|
||||
context.packetHandled = true
|
||||
val player = context.sender?.matteryPlayer ?: return
|
||||
|
||||
when (type) {
|
||||
Type.INVENTORY -> {
|
||||
if (slot in 0 until player.regularSlotFilters.size) {
|
||||
player.regularSlotFilters[slot].value = item
|
||||
}
|
||||
}
|
||||
|
||||
Type.EXOPACK -> {
|
||||
if (slot in 0 until player.exoPackSlotCount) {
|
||||
if (item == null) {
|
||||
player.exoPackSlotFilters.remove(slot)
|
||||
} else {
|
||||
player.exoPackSlotFilters[slot] = item
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
companion object {
|
||||
fun read(buff: FriendlyByteBuf): SetInventoryFilterPacket {
|
||||
return SetInventoryFilterPacket(buff.readEnum(Type::class.java), buff.readVarInt(), if (buff.readByte() > 0) buff.readItemType() else null)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
object MatteryPlayerNetworkChannel : MatteryNetworkChannel(
|
||||
version = "2",
|
||||
version = "3",
|
||||
name = "player"
|
||||
) {
|
||||
fun register() {
|
||||
@ -543,5 +595,7 @@ object MatteryPlayerNetworkChannel : MatteryNetworkChannel(
|
||||
|
||||
add(DisplayExosuitPacket::class, { DisplayExosuitPacket }, PLAY_TO_SERVER)
|
||||
add(HideExosuitPacket::class, { HideExosuitPacket }, PLAY_TO_SERVER)
|
||||
|
||||
add(SetInventoryFilterPacket::class, SetInventoryFilterPacket.Companion::read, PLAY_TO_SERVER)
|
||||
}
|
||||
}
|
||||
|
@ -2,6 +2,8 @@
|
||||
public-f net.minecraft.client.gui.screens.Screen f_96539_ # title
|
||||
public net.minecraft.server.MinecraftServer f_129744_ # storageSource
|
||||
|
||||
public net.minecraft.world.entity.player.Inventory f_150070_ # SELECTION_SIZE
|
||||
|
||||
# for accessing and setting from MatteryScreen class
|
||||
public net.minecraft.client.gui.screens.inventory.AbstractContainerScreen f_169600_
|
||||
public net.minecraft.client.gui.screens.inventory.AbstractContainerScreen f_169605_
|
||||
|
@ -533,46 +533,6 @@ function injectAtTail(path, instructions) {
|
||||
})
|
||||
}
|
||||
|
||||
function injectInventoryInsertHook(
|
||||
instructions,
|
||||
determinedOffset,
|
||||
hookName,
|
||||
hookDesc,
|
||||
pre
|
||||
) {
|
||||
var last = instructions.get(determinedOffset)
|
||||
|
||||
// print('Patching Inventory#add at instruction ' + determinedOffset + ' to add ' + hookName + hookDesc)
|
||||
|
||||
// loading this (Inventory) to stack
|
||||
|
||||
if (pre != undefined) {
|
||||
instructions.insert(last, pre)
|
||||
last = pre
|
||||
}
|
||||
|
||||
var next = new VarInsnNode(opcodesRemapped.aload, 0)
|
||||
instructions.insert(last, next)
|
||||
|
||||
last = next
|
||||
// loading itemstack to stack
|
||||
next = new VarInsnNode(opcodesRemapped.aload, 2)
|
||||
|
||||
instructions.insert(last, next)
|
||||
|
||||
last = next
|
||||
// dispatching hook method
|
||||
next = new MethodInsnNode(
|
||||
opcodesRemapped.invokestatic,
|
||||
'ru/dbotthepony/mc/otm/capability/MatteryPlayerCapability',
|
||||
hookName,
|
||||
hookDesc,
|
||||
false
|
||||
)
|
||||
|
||||
instructions.insert(last, next)
|
||||
}
|
||||
|
||||
function patchBlendFunc(node) {
|
||||
var last = new MethodInsnNode(
|
||||
opcodesRemapped.invokestatic,
|
||||
@ -795,128 +755,6 @@ function initializeCoreMod() {
|
||||
return node
|
||||
}),
|
||||
|
||||
'Inventory#add patch': {
|
||||
'target': {
|
||||
'type': 'METHOD',
|
||||
'class': 'net.minecraft.world.entity.player.Inventory',
|
||||
'methodName': ASMAPI.mapMethod('m_36040_'), // add
|
||||
'methodDesc': '(ILnet/minecraft/world/item/ItemStack;)Z'
|
||||
},
|
||||
|
||||
'transformer': function(node) {
|
||||
// If item is not "damaged":
|
||||
// 113: invokevirtual #144 // Method addResource:(ILnet/minecraft/world/item/ItemStack;)I
|
||||
// 116: invokevirtual #158 // Method net/minecraft/world/item/ItemStack.setCount:(I)V
|
||||
// 119: aload_2
|
||||
// 120: invokevirtual #57 // Method net/minecraft/world/item/ItemStack.isEmpty:()Z
|
||||
// 123: ifne 134
|
||||
// 126: aload_2
|
||||
// 127: invokevirtual #68 // Method net/minecraft/world/item/ItemStack.getCount:()I
|
||||
// 130: iload_3
|
||||
// 131: if_icmplt 87
|
||||
// <-- our target
|
||||
// 134: aload_2
|
||||
// 135: invokevirtual #68 // Method net/minecraft/world/item/ItemStack.getCount:()I
|
||||
// 138: iload_3
|
||||
// 139: if_icmpne 162
|
||||
// 142: aload_0
|
||||
|
||||
// If item returned that it is damaged:
|
||||
// 10: invokevirtual #97 // Method net/minecraft/world/item/ItemStack.isDamaged:()Z
|
||||
// 13: ifeq 87
|
||||
// 16: iload_1
|
||||
// 17: iconst_m1
|
||||
// 18: if_icmpne 26
|
||||
// 21: aload_0
|
||||
// 22: invokevirtual #86 // Method getFreeSlot:()I
|
||||
// 25: istore_1
|
||||
// <-- our target
|
||||
// 26: iload_1
|
||||
// 27: iflt 65
|
||||
// 30: aload_0
|
||||
// 31: getfield #19 // Field items:Lnet/minecraft/core/NonNullList;
|
||||
// 34: iload_1
|
||||
// 35: aload_2
|
||||
|
||||
var i
|
||||
|
||||
// 83: iconst_1
|
||||
// 84: ireturn
|
||||
// 85: iconst_0
|
||||
// 86: ireturn
|
||||
// 87: aload_2
|
||||
// 88: invokevirtual #68 // Method net/minecraft/world/item/ItemStack.getCount:()I
|
||||
|
||||
// patch "not damaged" before loop
|
||||
for (i = 0; i < node.instructions.size(); i++) {
|
||||
var determinedOffset = test([
|
||||
opcodesRemapped.iconst_1,
|
||||
opcodesRemapped.ireturn,
|
||||
opcodesRemapped.iconst_0,
|
||||
opcodesRemapped.ireturn,
|
||||
opcodesRemapped.aload,
|
||||
opcodesRemapped.invokevirtual,
|
||||
], node.instructions, i)
|
||||
|
||||
if (determinedOffset != -1) {
|
||||
injectInventoryInsertHook(node.instructions, determinedOffset - 1,
|
||||
'inventoryAddItemHookPre',
|
||||
'(Lnet/minecraft/world/entity/player/Inventory;Lnet/minecraft/world/item/ItemStack;)V')
|
||||
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
// patch "not damaged" after loop
|
||||
for (i = 0; i < node.instructions.size(); i++) {
|
||||
var determinedOffset = test([
|
||||
opcodesRemapped.invokevirtual,
|
||||
opcodesRemapped.invokevirtual,
|
||||
opcodesRemapped.aload,
|
||||
opcodesRemapped.invokevirtual,
|
||||
opcodesRemapped.ifne,
|
||||
opcodesRemapped.aload,
|
||||
opcodesRemapped.invokevirtual,
|
||||
opcodesRemapped.iload,
|
||||
opcodesRemapped.if_icmplt,
|
||||
], node.instructions, i)
|
||||
|
||||
if (determinedOffset != -1) {
|
||||
injectInventoryInsertHook(node.instructions, determinedOffset,
|
||||
'inventoryAddItemHook',
|
||||
'(Lnet/minecraft/world/entity/player/Inventory;Lnet/minecraft/world/item/ItemStack;)V')
|
||||
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
// patch "damaged"
|
||||
for (i = 0; i < node.instructions.size(); i++) {
|
||||
var determinedOffset = test([
|
||||
opcodesRemapped.invokevirtual,
|
||||
opcodesRemapped.ifeq,
|
||||
opcodesRemapped.iload,
|
||||
opcodesRemapped.iconst_m1,
|
||||
opcodesRemapped.if_icmpne,
|
||||
opcodesRemapped.aload,
|
||||
opcodesRemapped.invokevirtual,
|
||||
opcodesRemapped.istore,
|
||||
], node.instructions, i)
|
||||
|
||||
if (determinedOffset != -1) {
|
||||
injectInventoryInsertHook(node.instructions, determinedOffset,
|
||||
'inventoryAddDamagedItemHook',
|
||||
'(ILnet/minecraft/world/entity/player/Inventory;Lnet/minecraft/world/item/ItemStack;)V',
|
||||
new VarInsnNode(opcodesRemapped.iload, node.instructions.get(determinedOffset - 1)['var']))
|
||||
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
return node
|
||||
}
|
||||
},
|
||||
|
||||
'GameRenderer#render hook': {
|
||||
'target': {
|
||||
'type': 'METHOD',
|
||||
|
@ -9,6 +9,7 @@
|
||||
"MixinPatchProjectileFinder",
|
||||
"MixinLivingEntity",
|
||||
"MixinAnvilBlock",
|
||||
"MixinInventory",
|
||||
"MixinAbstractHurtingProjectile"
|
||||
]
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user