ContainerProxy, trick minecraft's click logic
This commit is contained in:
parent
b6bb0ed4b3
commit
9dfb534f9a
@ -0,0 +1,192 @@
|
||||
package ru.dbotthepony.mc.otm.container
|
||||
|
||||
import com.google.common.collect.ImmutableList
|
||||
import com.google.common.collect.ImmutableMap
|
||||
import com.google.common.collect.ImmutableSet
|
||||
import it.unimi.dsi.fastutil.ints.IntAVLTreeSet
|
||||
import it.unimi.dsi.fastutil.objects.Object2ObjectFunction
|
||||
import it.unimi.dsi.fastutil.objects.Reference2ObjectOpenHashMap
|
||||
import net.minecraft.world.Container
|
||||
import net.minecraft.world.entity.player.Player
|
||||
import net.minecraft.world.item.ItemStack
|
||||
import java.util.LinkedList
|
||||
import java.util.function.Consumer
|
||||
import java.util.function.Supplier
|
||||
import java.util.stream.Stream
|
||||
import kotlin.properties.ReadWriteProperty
|
||||
import kotlin.reflect.KProperty
|
||||
|
||||
open class ContainerProxy(containers: Stream<Pair<Container, Iterator<Int>>>) : Container {
|
||||
data class ContainerSlot(val container: Container, val outerIndex: Int, val index: Int) : ReadWriteProperty<Any, ItemStack>, Supplier<ItemStack>, Consumer<ItemStack> {
|
||||
var item: ItemStack
|
||||
get() = container[index]
|
||||
set(value) { container[index] = value }
|
||||
|
||||
override fun getValue(thisRef: Any, property: KProperty<*>): ItemStack {
|
||||
return item
|
||||
}
|
||||
|
||||
override fun setValue(thisRef: Any, property: KProperty<*>, value: ItemStack) {
|
||||
this.item = value
|
||||
}
|
||||
|
||||
override fun get(): ItemStack {
|
||||
return item
|
||||
}
|
||||
|
||||
override fun accept(t: ItemStack) {
|
||||
item = t
|
||||
}
|
||||
|
||||
val isEmpty: Boolean get() = item.isEmpty
|
||||
}
|
||||
|
||||
protected val slots: List<ContainerSlot>
|
||||
protected val slotsMap: Map<Container, List<ContainerSlot>>
|
||||
protected val containers: Set<Container>
|
||||
protected val fullCoverage: List<Container>
|
||||
protected val notFullCoverage: Map<Container, List<ContainerSlot>>
|
||||
|
||||
init {
|
||||
val list = ImmutableList.Builder<ContainerSlot>()
|
||||
var i = 0
|
||||
val validationMap = Reference2ObjectOpenHashMap<Container, IntAVLTreeSet>()
|
||||
val slotsMap = Reference2ObjectOpenHashMap<Container, ArrayList<ContainerSlot>>()
|
||||
|
||||
for ((container, slots) in containers) {
|
||||
val validator = validationMap.computeIfAbsent(container, Object2ObjectFunction { IntAVLTreeSet() })
|
||||
val slotList = slotsMap.computeIfAbsent(container, Object2ObjectFunction { ArrayList() })
|
||||
|
||||
for (slot in slots) {
|
||||
if (validator.add(slot)) {
|
||||
val slotObj = ContainerSlot(container, i++, slot)
|
||||
list.add(slotObj)
|
||||
slotList.add(slotObj)
|
||||
} else {
|
||||
throw IllegalArgumentException("Duplicate mapping for $container at $i for slot $slot")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
slots = list.build()
|
||||
this.containers = ImmutableSet.copyOf(validationMap.keys)
|
||||
|
||||
this.slotsMap = slotsMap.entries
|
||||
.stream()
|
||||
.map { it.key to ImmutableList.copyOf(it.value) }
|
||||
.collect(ImmutableMap.toImmutableMap({ it.first }, { it.second }))
|
||||
|
||||
this.fullCoverage = this.slotsMap.entries
|
||||
.stream()
|
||||
.filter { it.value.size == it.key.containerSize }
|
||||
.map { it.key }
|
||||
.collect(ImmutableList.toImmutableList())
|
||||
|
||||
this.notFullCoverage = this.slotsMap.entries
|
||||
.stream()
|
||||
.filter { it.key !in this.fullCoverage }
|
||||
.collect(ImmutableMap.toImmutableMap({ it.key }, { it.value }))
|
||||
}
|
||||
|
||||
override fun clearContent() {
|
||||
for (container in fullCoverage) {
|
||||
container.clearContent()
|
||||
}
|
||||
|
||||
for (slots in notFullCoverage.values) {
|
||||
for (slot in slots) {
|
||||
slot.item = ItemStack.EMPTY
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
override fun getContainerSize(): Int {
|
||||
return slots.size
|
||||
}
|
||||
|
||||
override fun isEmpty(): Boolean {
|
||||
for (container in fullCoverage)
|
||||
if (!container.isEmpty)
|
||||
return false
|
||||
|
||||
for (slots in notFullCoverage.values)
|
||||
for (slot in slots)
|
||||
if (!slot.isEmpty)
|
||||
return false
|
||||
|
||||
return true
|
||||
}
|
||||
|
||||
fun slotAt(index: Int): ContainerSlot {
|
||||
return slots[index]
|
||||
}
|
||||
|
||||
override fun getItem(index: Int): ItemStack {
|
||||
// do not violate contract of getItem not throwing exceptions when index is invalid
|
||||
return slots.getOrNull(index)?.item ?: ItemStack.EMPTY
|
||||
}
|
||||
|
||||
override fun removeItem(index: Int, count: Int): ItemStack {
|
||||
val data = slots.getOrNull(index) ?: return ItemStack.EMPTY
|
||||
return data.container.removeItem(data.index, count)
|
||||
}
|
||||
|
||||
override fun removeItemNoUpdate(index: Int): ItemStack {
|
||||
val data = slots[index]
|
||||
return data.container.removeItemNoUpdate(data.index)
|
||||
}
|
||||
|
||||
override fun setItem(index: Int, value: ItemStack) {
|
||||
slots[index].item = value
|
||||
}
|
||||
|
||||
override fun setChanged() {
|
||||
for (container in containers) {
|
||||
container.setChanged()
|
||||
}
|
||||
}
|
||||
|
||||
override fun stillValid(player: Player): Boolean {
|
||||
for (container in containers)
|
||||
if (!container.stillValid(player))
|
||||
return false
|
||||
|
||||
return true
|
||||
}
|
||||
|
||||
class Builder {
|
||||
private var built = false
|
||||
private val values = LinkedList<Pair<Container, Iterator<Int>>>()
|
||||
|
||||
fun add(container: Container): Builder {
|
||||
check(!built) { "Already built!" }
|
||||
values.add(container to (0 until container.containerSize).iterator())
|
||||
return this
|
||||
}
|
||||
|
||||
fun ofRange(container: Container, from: Int = 0, to: Int = container.containerSize - 1): Builder {
|
||||
check(!built) { "Already built!" }
|
||||
values.add(container to (from .. to).iterator())
|
||||
return this
|
||||
}
|
||||
|
||||
fun of(container: Container, slots: Iterator<Int>): Builder {
|
||||
check(!built) { "Already built!" }
|
||||
values.add(container to slots)
|
||||
return this
|
||||
}
|
||||
|
||||
fun of(container: Container, slots: Iterable<Int>): Builder {
|
||||
check(!built) { "Already built!" }
|
||||
values.add(container to slots.iterator())
|
||||
return this
|
||||
}
|
||||
|
||||
fun build(): ContainerProxy {
|
||||
check(!built) { "Already built!" }
|
||||
val value = ContainerProxy(values.stream())
|
||||
values.clear()
|
||||
return value
|
||||
}
|
||||
}
|
||||
}
|
@ -20,6 +20,7 @@ import ru.dbotthepony.mc.otm.client.screen.panels.EditablePanel
|
||||
import ru.dbotthepony.mc.otm.client.screen.panels.SlotPanel
|
||||
import ru.dbotthepony.mc.otm.compat.cos.cosmeticArmorSlots
|
||||
import ru.dbotthepony.mc.otm.compat.curios.isCurioSlot
|
||||
import ru.dbotthepony.mc.otm.container.ContainerProxy
|
||||
import ru.dbotthepony.mc.otm.container.ItemFilter
|
||||
import ru.dbotthepony.mc.otm.container.ItemFilterNetworkSlot
|
||||
import ru.dbotthepony.mc.otm.menu.widget.AbstractWidget
|
||||
@ -158,39 +159,41 @@ abstract class MatteryMenu @JvmOverloads protected constructor(
|
||||
|
||||
protected fun addInventorySlots(autoFrame: Boolean = !ply.isSpectator) {
|
||||
check(_playerInventorySlots.isEmpty()) { "Already created inventory slots" }
|
||||
autoCreateInventoryFrame = autoFrame
|
||||
|
||||
for (i in 9 .. 35) {
|
||||
val slot = InventorySlot(inventory, i)
|
||||
|
||||
_playerInventorySlots.add(slot)
|
||||
_playerMainSlots.add(slot)
|
||||
_playerCombinedInventorySlots.add(slot)
|
||||
addSlot(slot)
|
||||
}
|
||||
|
||||
val mattery = ply.matteryPlayer
|
||||
|
||||
// trick minecraft's code into thinking that slots come from contiguous container
|
||||
val proxyBuilder = ContainerProxy.Builder()
|
||||
|
||||
proxyBuilder.of(inventory, 0 .. 35)
|
||||
|
||||
if (mattery != null && mattery.hasExoSuit) {
|
||||
for (i in 0 until mattery.exoSuitContainer.containerSize) {
|
||||
val slot = InventorySlot(mattery.exoSuitContainer, i)
|
||||
proxyBuilder.add(mattery.exoSuitContainer)
|
||||
}
|
||||
|
||||
val proxy = proxyBuilder.build()
|
||||
|
||||
autoCreateInventoryFrame = autoFrame
|
||||
|
||||
for (slotId in 0 until proxy.containerSize) {
|
||||
val slot = InventorySlot(proxy, slotId)
|
||||
|
||||
if (slotId in 0 .. 8) {
|
||||
_playerHotbarSlots.add(slot)
|
||||
addSlot(slot)
|
||||
continue
|
||||
} else if (slotId in 9 .. 35) {
|
||||
_playerMainSlots.add(slot)
|
||||
} else {
|
||||
_playerExoSuitSlots.add(slot)
|
||||
}
|
||||
|
||||
_playerInventorySlots.add(slot)
|
||||
_playerExoSuitSlots.add(slot)
|
||||
_playerCombinedInventorySlots.add(slot)
|
||||
addSlot(slot)
|
||||
}
|
||||
}
|
||||
|
||||
for (i in 0..8) {
|
||||
val slot = InventorySlot(inventory, i)
|
||||
|
||||
addSlot(slot)
|
||||
_playerInventorySlots.add(slot)
|
||||
_playerHotbarSlots.add(slot)
|
||||
}
|
||||
}
|
||||
|
||||
override fun broadcastChanges() {
|
||||
for (widget in _matteryWidgets) {
|
||||
widget.updateServer()
|
||||
|
Loading…
Reference in New Issue
Block a user