224 lines
6.9 KiB
Kotlin
224 lines
6.9 KiB
Kotlin
package net.shadowfacts.extrahoppers.block.base
|
|
|
|
import net.minecraft.block.entity.BlockEntity
|
|
import net.minecraft.block.entity.BlockEntityType
|
|
import net.minecraft.block.entity.Hopper
|
|
import net.minecraft.block.entity.HopperBlockEntity
|
|
import net.minecraft.entity.Entity
|
|
import net.minecraft.entity.ItemEntity
|
|
import net.minecraft.entity.player.PlayerEntity
|
|
import net.minecraft.inventory.Inventories
|
|
import net.minecraft.inventory.Inventory
|
|
import net.minecraft.inventory.SidedInventory
|
|
import net.minecraft.item.ItemStack
|
|
import net.minecraft.nbt.CompoundTag
|
|
import net.minecraft.state.property.DirectionProperty
|
|
import net.minecraft.util.BooleanBiFunction
|
|
import net.minecraft.util.DefaultedList
|
|
import net.minecraft.util.Tickable
|
|
import net.minecraft.util.math.Direction
|
|
import net.minecraft.util.shape.VoxelShapes
|
|
import net.shadowfacts.extrahoppers.util.toVec3d
|
|
|
|
/**
|
|
* @author shadowfacts
|
|
*/
|
|
abstract class BaseHopperBlockEntity(type: BlockEntityType<*>): BlockEntity(type), Inventory, Hopper, Tickable {
|
|
|
|
abstract val inventorySize: Int
|
|
abstract val maxTransferCooldown: Int
|
|
abstract val facingProp: DirectionProperty
|
|
abstract val inputSide: Direction
|
|
val inventory: DefaultedList<ItemStack> by lazy { DefaultedList.ofSize(inventorySize, ItemStack.EMPTY) }
|
|
protected var transferCooldown = -1
|
|
|
|
override fun toTag(tag: CompoundTag): CompoundTag {
|
|
Inventories.toTag(tag, inventory)
|
|
tag.putInt("transferCooldown", transferCooldown)
|
|
return super.toTag(tag)
|
|
}
|
|
|
|
override fun fromTag(tag: CompoundTag) {
|
|
super.fromTag(tag)
|
|
inventory.clear()
|
|
Inventories.fromTag(tag, inventory)
|
|
transferCooldown = tag.getInt("transferCooldown")
|
|
}
|
|
|
|
override fun tick() {
|
|
val world = world
|
|
if (world == null || world.isClient) return
|
|
|
|
transferCooldown -= 1
|
|
if (transferCooldown <= 0) {
|
|
transferCooldown = 0
|
|
insertAndExtract()
|
|
}
|
|
}
|
|
|
|
fun onEntityCollision(entity: Entity) {
|
|
if (entity is ItemEntity) {
|
|
if (VoxelShapes.matchesAnywhere(VoxelShapes.cuboid(entity.boundingBox.offset(-pos.x.toDouble(), -pos.y.toDouble(), -pos.z.toDouble())), inputAreaShape, BooleanBiFunction.AND)) {
|
|
insertAndExtract {
|
|
HopperBlockEntity.extract(this, entity)
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
fun insertAndExtract(extractor: (() -> Boolean)? = null) {
|
|
val world = world
|
|
if (world == null || world.isClient) return
|
|
|
|
var didWork = false
|
|
if (!isInvEmpty && insert()) {
|
|
didWork = true
|
|
}
|
|
if (!isFull() && (extractor != null && extractor()) || extract()) {
|
|
didWork = true
|
|
}
|
|
|
|
if (didWork) {
|
|
transferCooldown = maxTransferCooldown
|
|
markDirty()
|
|
}
|
|
}
|
|
|
|
fun insert(): Boolean {
|
|
val outputInv = getOutputInventory() ?: return false
|
|
|
|
val insertionSide = cachedState.get(facingProp).opposite
|
|
if (isInventoryFull(outputInv, insertionSide)) return false
|
|
|
|
for (slot in 0 until invSize) {
|
|
if (getInvStack(slot).isEmpty) continue
|
|
|
|
val stackCopy = getInvStack(slot).copy()
|
|
|
|
val remaining = HopperBlockEntity.transfer(this, outputInv, takeInvStack(slot, 1), insertionSide)
|
|
if (remaining.isEmpty) {
|
|
outputInv.markDirty()
|
|
return true
|
|
}
|
|
|
|
setInvStack(slot, stackCopy)
|
|
}
|
|
|
|
return false
|
|
}
|
|
|
|
fun extract(): Boolean {
|
|
val inputInv = getInputInventory()
|
|
return if (inputInv != null) {
|
|
val extractionSide = inputSide.opposite
|
|
if (!isInventoryEmpty(inputInv, extractionSide)) {
|
|
inventorySlots(inputInv, extractionSide).any { slot ->
|
|
extractFromInv(inputInv, slot, extractionSide)
|
|
}
|
|
} else {
|
|
false
|
|
}
|
|
} else {
|
|
HopperBlockEntity.getInputItemEntities(this).any {
|
|
HopperBlockEntity.extract(this, it)
|
|
}
|
|
}
|
|
}
|
|
|
|
fun extractFromInv(inventory: Inventory, slot: Int, extractonSide: Direction): Boolean {
|
|
val stack = inventory.getInvStack(slot)
|
|
if (!stack.isEmpty && canExtract(inventory, stack, slot, extractonSide)) {
|
|
val stackCopy = stack.copy()
|
|
val remaining = HopperBlockEntity.transfer(inventory, this, inventory.takeInvStack(slot, 1), null)
|
|
if (remaining.isEmpty) {
|
|
inventory.markDirty()
|
|
return true
|
|
}
|
|
|
|
inventory.setInvStack(slot, stackCopy)
|
|
}
|
|
|
|
return false
|
|
}
|
|
|
|
fun isFull(): Boolean {
|
|
return inventory.none(ItemStack::isEmpty)
|
|
}
|
|
|
|
fun getOutputInventory(): Inventory? {
|
|
val facing = cachedState.get(facingProp)
|
|
return HopperBlockEntity.getInventoryAt(world!!, pos.offset(facing))
|
|
}
|
|
|
|
fun getInputInventory(): Inventory? {
|
|
return HopperBlockEntity.getInventoryAt(world!!, pos.offset(inputSide))
|
|
}
|
|
|
|
fun inventorySlots(inv: Inventory, side: Direction): Sequence<Int> {
|
|
return if (inv is SidedInventory) {
|
|
inv.getInvAvailableSlots(side).asSequence()
|
|
} else {
|
|
(0 until inv.invSize).asSequence()
|
|
}
|
|
}
|
|
|
|
fun isInventoryFull(inv: Inventory, side: Direction): Boolean {
|
|
val slots = inventorySlots(inv, side)
|
|
return slots.map(inv::getInvStack).none(ItemStack::isEmpty)
|
|
}
|
|
|
|
fun isInventoryEmpty(inv: Inventory, side: Direction): Boolean {
|
|
val slots = inventorySlots(inv, side)
|
|
return slots.map(inv::getInvStack).all(ItemStack::isEmpty)
|
|
}
|
|
|
|
fun canExtract(inv: Inventory, stack: ItemStack, slot: Int, extractionSide: Direction): Boolean {
|
|
return if (inv is SidedInventory) {
|
|
inv.canExtractInvStack(slot, stack, extractionSide)
|
|
} else {
|
|
true
|
|
}
|
|
}
|
|
|
|
// Inventory
|
|
|
|
|
|
override fun getInvSize() = inventory.size
|
|
|
|
override fun takeInvStack(slot: Int, amount: Int): ItemStack {
|
|
return Inventories.splitStack(inventory, slot, amount)
|
|
}
|
|
|
|
override fun isInvEmpty() = inventory.isEmpty()
|
|
|
|
override fun getInvStack(slot: Int) = inventory[slot]
|
|
|
|
override fun clear() {
|
|
inventory.clear()
|
|
}
|
|
|
|
override fun setInvStack(slot: Int, stack: ItemStack) {
|
|
inventory[slot] = stack
|
|
}
|
|
|
|
override fun removeInvStack(slot: Int): ItemStack {
|
|
return Inventories.removeStack(inventory, slot)
|
|
}
|
|
|
|
override fun canPlayerUseInv(player: PlayerEntity): Boolean {
|
|
return if (world?.getBlockEntity(pos) != this) {
|
|
false
|
|
} else {
|
|
val distance = player.squaredDistanceTo(this.pos.toVec3d())
|
|
distance < 64
|
|
}
|
|
}
|
|
|
|
// Hopper
|
|
|
|
override fun getHopperX() = pos.x.toDouble()
|
|
override fun getHopperY() = pos.y.toDouble()
|
|
override fun getHopperZ() = pos.z.toDouble()
|
|
|
|
}
|