Compare commits

...

3 Commits

Author SHA1 Message Date
Shadowfacts 5e42cbddce
Add Inverted Golden Hopper 2020-03-28 18:29:00 -04:00
Shadowfacts e9e28f5625
Add Golden Hopper 2020-03-28 14:26:39 -04:00
Shadowfacts 7b134f688d
Extra base hopper code 2020-03-28 13:57:54 -04:00
31 changed files with 1023 additions and 302 deletions

View File

@ -3,6 +3,7 @@ package net.shadowfacts.extrahoppers
import net.fabricmc.api.ModInitializer import net.fabricmc.api.ModInitializer
import net.fabricmc.fabric.api.container.ContainerFactory import net.fabricmc.fabric.api.container.ContainerFactory
import net.fabricmc.fabric.api.container.ContainerProviderRegistry import net.fabricmc.fabric.api.container.ContainerProviderRegistry
import net.shadowfacts.extrahoppers.block.gold.GoldHopperContainer
import net.shadowfacts.extrahoppers.block.wood.WoodHopperContainer import net.shadowfacts.extrahoppers.block.wood.WoodHopperContainer
import net.shadowfacts.extrahoppers.init.EHBlockEntities import net.shadowfacts.extrahoppers.init.EHBlockEntities
import net.shadowfacts.extrahoppers.init.EHBlocks import net.shadowfacts.extrahoppers.init.EHBlocks
@ -16,6 +17,7 @@ object ExtraHoppers: ModInitializer {
EHItems.init() EHItems.init()
ContainerProviderRegistry.INSTANCE.registerFactory(WoodHopperContainer.ID, ContainerFactory(WoodHopperContainer.Companion::create)) ContainerProviderRegistry.INSTANCE.registerFactory(WoodHopperContainer.ID, ContainerFactory(WoodHopperContainer.Companion::create))
ContainerProviderRegistry.INSTANCE.registerFactory(GoldHopperContainer.ID, ContainerFactory(GoldHopperContainer.Companion::create))
} }
} }

View File

@ -3,6 +3,8 @@ package net.shadowfacts.extrahoppers
import net.fabricmc.api.ClientModInitializer import net.fabricmc.api.ClientModInitializer
import net.fabricmc.fabric.api.client.screen.ContainerScreenFactory import net.fabricmc.fabric.api.client.screen.ContainerScreenFactory
import net.fabricmc.fabric.api.client.screen.ScreenProviderRegistry import net.fabricmc.fabric.api.client.screen.ScreenProviderRegistry
import net.shadowfacts.extrahoppers.block.gold.GoldHopperContainer
import net.shadowfacts.extrahoppers.block.gold.GoldHopperScreen
import net.shadowfacts.extrahoppers.block.wood.WoodHopperContainer import net.shadowfacts.extrahoppers.block.wood.WoodHopperContainer
import net.shadowfacts.extrahoppers.block.wood.WoodHopperScreen import net.shadowfacts.extrahoppers.block.wood.WoodHopperScreen
@ -10,6 +12,7 @@ object ExtraHoppersClient: ClientModInitializer {
override fun onInitializeClient() { override fun onInitializeClient() {
ScreenProviderRegistry.INSTANCE.registerFactory(WoodHopperContainer.ID, ContainerScreenFactory(WoodHopperScreen.Companion::create)) ScreenProviderRegistry.INSTANCE.registerFactory(WoodHopperContainer.ID, ContainerScreenFactory(WoodHopperScreen.Companion::create))
ScreenProviderRegistry.INSTANCE.registerFactory(GoldHopperContainer.ID, ContainerScreenFactory(GoldHopperScreen.Companion::create))
} }
} }

View File

@ -0,0 +1,91 @@
package net.shadowfacts.extrahoppers.block.base
import net.minecraft.block.Block
import net.minecraft.block.BlockPlacementEnvironment
import net.minecraft.block.BlockState
import net.minecraft.block.entity.Hopper
import net.minecraft.entity.EntityContext
import net.minecraft.item.ItemPlacementContext
import net.minecraft.state.StateManager
import net.minecraft.state.property.Properties
import net.minecraft.util.BooleanBiFunction
import net.minecraft.util.ItemScatterer
import net.minecraft.util.math.BlockPos
import net.minecraft.util.math.Direction
import net.minecraft.util.shape.VoxelShape
import net.minecraft.util.shape.VoxelShapes
import net.minecraft.world.BlockView
import net.minecraft.world.World
/**
* @author shadowfacts
*/
abstract class BaseHopperBlock<T: BaseHopperBlockEntity>(settings: Settings): BlockWithEntity<T>(settings) {
companion object {
val FACING = Properties.HOPPER_FACING
// todo: redstone support
// val ENABLED = Properties.ENABLED
val TOP_SHAPE = createCuboidShape(0.0, 10.0, 0.0, 16.0, 16.0, 16.0)
val MIDDLE_SHAPE = createCuboidShape(4.0, 4.0, 4.0, 12.0, 10.0, 12.0)
val OUTSIDE_SHAPE = VoxelShapes.union(MIDDLE_SHAPE, TOP_SHAPE)
val DEFAULT_SHAPE = VoxelShapes.combineAndSimplify(OUTSIDE_SHAPE, Hopper.INSIDE_SHAPE, BooleanBiFunction.ONLY_FIRST)
val DOWN_SHAPE = VoxelShapes.union(DEFAULT_SHAPE, createCuboidShape(6.0, 0.0, 6.0, 10.0, 4.0, 10.0))
val EAST_SHAPE = VoxelShapes.union(DEFAULT_SHAPE, createCuboidShape(12.0, 4.0, 6.0, 16.0, 8.0, 10.0))
val NORTH_SHAPE = VoxelShapes.union(DEFAULT_SHAPE, createCuboidShape(6.0, 4.0, 0.0, 10.0, 8.0, 4.0))
val SOUTH_SHAPE = VoxelShapes.union(DEFAULT_SHAPE, createCuboidShape(6.0, 4.0, 12.0, 10.0, 8.0, 16.0))
val WEST_SHAPE = VoxelShapes.union(DEFAULT_SHAPE, createCuboidShape(0.0, 4.0, 6.0, 4.0, 8.0, 10.0))
val DOWN_RAY_TRACE_SHAPE = Hopper.INSIDE_SHAPE
val EAST_RAY_TRACE_SHAPE = VoxelShapes.union(Hopper.INSIDE_SHAPE, createCuboidShape(12.0, 8.0, 6.0, 16.0, 10.0, 10.0))
val NORTH_RAY_TRACE_SHAPE = VoxelShapes.union(Hopper.INSIDE_SHAPE, createCuboidShape(6.0, 8.0, 0.0, 10.0, 10.0, 4.0))
val SOUTH_RAY_TRACE_SHAPE = VoxelShapes.union(Hopper.INSIDE_SHAPE, createCuboidShape(6.0, 8.0, 12.0, 10.0, 10.0, 16.0))
val WEST_RAY_TRACE_SHAPE = VoxelShapes.union(Hopper.INSIDE_SHAPE, createCuboidShape(0.0, 8.0, 6.0, 4.0, 10.0, 10.0))
}
override fun appendProperties(builder: StateManager.Builder<Block, BlockState>) {
builder.add(FACING)
}
override fun getOutlineShape(state: BlockState, world: BlockView, pos: BlockPos, entityContext: EntityContext): VoxelShape {
return when (state.get(FACING)) {
Direction.DOWN -> DOWN_SHAPE
Direction.NORTH -> NORTH_SHAPE
Direction.SOUTH -> SOUTH_SHAPE
Direction.WEST -> WEST_SHAPE
Direction.EAST -> EAST_SHAPE
else -> DEFAULT_SHAPE
}
}
override fun getRayTraceShape(state: BlockState, world: BlockView, pos: BlockPos): VoxelShape {
return when (state.get(FACING)) {
Direction.DOWN -> DOWN_RAY_TRACE_SHAPE
Direction.NORTH -> NORTH_RAY_TRACE_SHAPE
Direction.SOUTH -> SOUTH_RAY_TRACE_SHAPE
Direction.WEST -> WEST_RAY_TRACE_SHAPE
Direction.EAST -> EAST_RAY_TRACE_SHAPE
else -> Hopper.INSIDE_SHAPE
}
}
override fun getPlacementState(context: ItemPlacementContext): BlockState {
val hitFacing = context.side.opposite
val facing = if (hitFacing.axis == Direction.Axis.Y) Direction.DOWN else hitFacing
return defaultState.with(FACING, facing)
}
override fun onBlockRemoved(oldState: BlockState, world: World, pos: BlockPos, newState: BlockState, bl: Boolean) {
if (oldState.block != newState.block) {
getBlockEntity(world, pos)?.also {
ItemScatterer.spawn(world, pos, it)
world.updateHorizontalAdjacent(pos, this)
}
super.onBlockRemoved(oldState, world, pos, newState, bl)
}
}
override fun canPlaceAtSide(blockState: BlockState?, blockView: BlockView?, blockPos: BlockPos?, blockPlacementEnvironment: BlockPlacementEnvironment?): Boolean {
return false
}
}

View File

@ -0,0 +1,223 @@
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()
}

View File

@ -0,0 +1,69 @@
package net.shadowfacts.extrahoppers.block.base
import net.minecraft.container.Container
import net.minecraft.container.Slot
import net.minecraft.entity.player.PlayerEntity
import net.minecraft.entity.player.PlayerInventory
import net.minecraft.item.ItemStack
/**
* @author shadowfacts
*/
abstract class BaseHopperContainer<T: BaseHopperBlockEntity>(
syncId: Int,
playerInv: PlayerInventory,
val hopper: T
): Container(null, syncId) {
init {
addHopperSlots()
// player inv
for (y in 0 until 3) {
for (x in 0 until 9) {
addSlot(Slot(playerInv, x + y * 9 + 9, 8 + x * 18, 51 + y * 18))
}
}
// hotbar
for (x in 0 until 9) {
addSlot(Slot(playerInv, x, 8 + x * 18, 109))
}
}
abstract fun addHopperSlots()
override fun canUse(player: PlayerEntity): Boolean {
return true
}
override fun close(player: PlayerEntity) {
super.close(player)
hopper.onInvClose(player)
}
override fun transferSlot(player: PlayerEntity, slotIndex: Int): ItemStack {
var remaining = ItemStack.EMPTY
val slot = slots[slotIndex]
if (slot != null && slot.hasStack()) {
val slotStack = slot.stack
remaining = slotStack.copy()
if (slotIndex < hopper.invSize) {
if (!insertItem(slotStack, hopper.invSize, slots.size, true)) {
return ItemStack.EMPTY;
}
} else if (!insertItem(slotStack, 0, hopper.invSize, false)) {
return ItemStack.EMPTY;
}
if (slotStack.isEmpty) {
slot.stack = ItemStack.EMPTY;
} else {
slot.markDirty()
}
}
return remaining
}
}

View File

@ -0,0 +1,43 @@
package net.shadowfacts.extrahoppers.block.base
import com.mojang.blaze3d.platform.GlStateManager
import net.minecraft.client.gui.screen.ingame.ContainerScreen
import net.minecraft.entity.player.PlayerInventory
import net.minecraft.text.Text
import net.minecraft.util.Identifier
/**
* @author shadowfacts
*/
abstract class BaseHopperScreen<T: BaseHopperContainer<*>>(
container: T,
playerInv: PlayerInventory,
title: Text
): ContainerScreen<T>(container, playerInv, title) {
abstract val background: Identifier
init {
containerHeight = 133
}
override fun render(i: Int, j: Int, f: Float) {
this.renderBackground()
super.render(i, j, f)
drawMouseoverTooltip(i, j)
}
override fun drawForeground(mouseX: Int, mouseY: Int) {
font.draw(title.asFormattedString(), 8f, 6f, 0x404040)
font.draw(playerInventory.displayName.asFormattedString(), 8f, containerHeight - 94f, 0x404040)
}
override fun drawBackground(delta: Float, mouseX: Int, mouseY: Int) {
GlStateManager.color4f(1f, 1f, 1f, 1f)
minecraft!!.textureManager.bindTexture(background)
val x = (width - containerWidth) / 2
val y = (height - containerHeight) / 2
blit(x, y, 0, 0, containerWidth, containerHeight)
}
}

View File

@ -0,0 +1,46 @@
package net.shadowfacts.extrahoppers.block.gold
import net.fabricmc.fabric.api.block.FabricBlockSettings
import net.fabricmc.fabric.api.container.ContainerProviderRegistry
import net.minecraft.block.BlockState
import net.minecraft.block.Material
import net.minecraft.entity.player.PlayerEntity
import net.minecraft.sound.BlockSoundGroup
import net.minecraft.stat.Stats
import net.minecraft.util.ActionResult
import net.minecraft.util.Hand
import net.minecraft.util.Identifier
import net.minecraft.util.hit.BlockHitResult
import net.minecraft.util.math.BlockPos
import net.minecraft.world.BlockView
import net.minecraft.world.World
import net.shadowfacts.extrahoppers.block.base.BaseHopperBlock
/**
* @author shadowfacts
*/
open class GoldHopperBlock: BaseHopperBlock<GoldHopperBlockEntity>(
FabricBlockSettings.of(Material.METAL)
.strength(3f, 6f)
.sounds(BlockSoundGroup.METAL)
.nonOpaque()
.build()
) {
companion object {
val ID = Identifier("extrahoppers", "gold_hopper")
}
override fun createBlockEntity(world: BlockView) = GoldHopperBlockEntity(false)
override fun onUse(state: BlockState, world: World, pos: BlockPos, player: PlayerEntity, hand: Hand, hitResult: BlockHitResult): ActionResult {
if (!world.isClient) {
player.incrementStat(Stats.INSPECT_HOPPER)
ContainerProviderRegistry.INSTANCE.openContainer(GoldHopperContainer.ID, player) { buf ->
buf.writeBlockPos(pos)
}
}
return ActionResult.SUCCESS
}
}

View File

@ -0,0 +1,52 @@
package net.shadowfacts.extrahoppers.block.gold
import net.minecraft.block.Block
import net.minecraft.block.entity.Hopper
import net.minecraft.nbt.CompoundTag
import net.minecraft.util.math.Direction
import net.minecraft.util.shape.VoxelShape
import net.minecraft.util.shape.VoxelShapes
import net.shadowfacts.extrahoppers.block.base.BaseHopperBlock
import net.shadowfacts.extrahoppers.block.base.BaseHopperBlockEntity
import net.shadowfacts.extrahoppers.init.EHBlockEntities
/**
* @author shadowfacts
*/
open class GoldHopperBlockEntity(var inverted: Boolean): BaseHopperBlockEntity(
if (inverted) EHBlockEntities.INVERTED_GOLD_HOPPER else EHBlockEntities.GOLD_HOPPER
) {
companion object {
val INVERTED_INSIDE_SHAPE = Block.createCuboidShape(2.0, 5.0, 2.0, 14.0, 0.0, 14.0)
val INVERTED_BELOW_SHAPE = Block.createCuboidShape(0.0, 0.0, 0.0, 16.0, -16.0, 16.0)
val INVERTED_INPUT_AREA_SHAPE = VoxelShapes.union(INVERTED_INSIDE_SHAPE, INVERTED_BELOW_SHAPE)
}
override val inventorySize = 5
override val maxTransferCooldown = 4
override val facingProp = if (inverted) InvertedGoldHopperBlock.INVERTED_FACING else BaseHopperBlock.FACING
override val inputSide = if (inverted) Direction.DOWN else Direction.UP
@Deprecated("only used for deserializing")
constructor(): this(false)
override fun toTag(tag: CompoundTag): CompoundTag {
tag.putBoolean("inverted", inverted)
return super.toTag(tag)
}
override fun fromTag(tag: CompoundTag) {
super.fromTag(tag)
inverted = tag.getBoolean("inverted")
}
override fun getInputAreaShape(): VoxelShape {
return if (inverted) {
INVERTED_INPUT_AREA_SHAPE
} else {
Hopper.INPUT_AREA_SHAPE
}
}
}

View File

@ -0,0 +1,36 @@
package net.shadowfacts.extrahoppers.block.gold
import net.minecraft.container.Slot
import net.minecraft.entity.player.PlayerEntity
import net.minecraft.entity.player.PlayerInventory
import net.minecraft.util.Identifier
import net.minecraft.util.PacketByteBuf
import net.shadowfacts.extrahoppers.block.base.BaseHopperContainer
import net.shadowfacts.extrahoppers.init.EHBlocks
/**
* @author shadowfacts
*/
class GoldHopperContainer(
syncId: Int,
playerInv: PlayerInventory,
hopper: GoldHopperBlockEntity
): BaseHopperContainer<GoldHopperBlockEntity>(syncId, playerInv, hopper) {
companion object {
val ID = Identifier("extrahoppers", "gold_hopper")
fun create(syncId: Int, identifier: Identifier, player: PlayerEntity, buf: PacketByteBuf): GoldHopperContainer {
val pos = buf.readBlockPos()
val blockEntity = EHBlocks.GOLD_HOPPER.getBlockEntity(player.world, pos)!!
return GoldHopperContainer(syncId, player.inventory, blockEntity)
}
}
override fun addHopperSlots() {
for (i in 0 until 5) {
addSlot(Slot(hopper, i, 44 + i * 18, 20))
}
}
}

View File

@ -0,0 +1,24 @@
package net.shadowfacts.extrahoppers.block.gold
import net.minecraft.client.MinecraftClient
import net.minecraft.entity.player.PlayerInventory
import net.minecraft.text.TranslatableText
import net.minecraft.util.Identifier
import net.shadowfacts.extrahoppers.block.base.BaseHopperScreen
/**
* @author shadowfacts
*/
class GoldHopperScreen(
container: GoldHopperContainer,
playerInv: PlayerInventory
): BaseHopperScreen<GoldHopperContainer>(container, playerInv, TranslatableText("block.extrahoppers.${if (container.hopper.inverted) "inverted_" else ""}gold_hopper")) {
companion object {
fun create(container: GoldHopperContainer): GoldHopperScreen {
return GoldHopperScreen(container, MinecraftClient.getInstance().player!!.inventory)
}
}
override val background = Identifier("minecraft", "textures/gui/container/hopper.png")
}

View File

@ -0,0 +1,78 @@
package net.shadowfacts.extrahoppers.block.gold
import net.minecraft.block.Block
import net.minecraft.block.BlockState
import net.minecraft.entity.EntityContext
import net.minecraft.item.ItemPlacementContext
import net.minecraft.state.StateManager
import net.minecraft.state.property.DirectionProperty
import net.minecraft.util.BooleanBiFunction
import net.minecraft.util.Identifier
import net.minecraft.util.math.BlockPos
import net.minecraft.util.math.Direction
import net.minecraft.util.shape.VoxelShape
import net.minecraft.util.shape.VoxelShapes
import net.minecraft.world.BlockView
/**
* @author shadowfacts
*/
class InvertedGoldHopperBlock: GoldHopperBlock() {
companion object {
val ID = Identifier("extrahoppers", "inverted_gold_hopper")
val INVERTED_FACING = DirectionProperty.of("inverted_facing") { direction: Direction -> direction != Direction.DOWN }
val INVERTED_TOP_SHAPE = createCuboidShape(0.0, 6.0, 0.0, 16.0, 0.0, 16.0)
val INVERTED_MIDDLE_SHAPE = createCuboidShape(4.0, 12.0, 4.0, 12.0, 6.0, 12.0)
val INVERTED_INSIDE_SHAPE = createCuboidShape(2.0, 5.0, 2.0, 14.0, 0.0, 14.0)
val INVERTED_OUTSIDE_SHAPE = VoxelShapes.union(INVERTED_MIDDLE_SHAPE, INVERTED_TOP_SHAPE)
val INVERTED_DEFAULT_SHAPE = VoxelShapes.combineAndSimplify(INVERTED_OUTSIDE_SHAPE, INVERTED_INSIDE_SHAPE, BooleanBiFunction.ONLY_FIRST)
val INVERTED_DOWN_SHAPE = VoxelShapes.union(INVERTED_DEFAULT_SHAPE, createCuboidShape(6.0, 16.0, 6.0, 10.0, 12.0, 10.0))
val INVERTED_EAST_SHAPE = VoxelShapes.union(INVERTED_DEFAULT_SHAPE, createCuboidShape(12.0, 12.0, 6.0, 16.0, 8.0, 10.0))
val INVERTED_NORTH_SHAPE = VoxelShapes.union(INVERTED_DEFAULT_SHAPE, createCuboidShape(6.0, 12.0, 0.0, 10.0, 8.0, 4.0))
val INVERTED_SOUTH_SHAPE = VoxelShapes.union(INVERTED_DEFAULT_SHAPE, createCuboidShape(6.0, 12.0, 12.0, 10.0, 8.0, 16.0))
val INVERTED_WEST_SHAPE = VoxelShapes.union(INVERTED_DEFAULT_SHAPE, createCuboidShape(0.0, 12.0, 6.0, 4.0, 8.0, 10.0))
val INVERTED_DOWN_RAY_TRACE_SHAPE = INVERTED_INSIDE_SHAPE
val INVERTED_EAST_RAY_TRACE_SHAPE = VoxelShapes.union(INVERTED_INSIDE_SHAPE, createCuboidShape(12.0, 8.0, 6.0, 16.0, 6.0, 10.0))
val INVERTED_NORTH_RAY_TRACE_SHAPE = VoxelShapes.union(INVERTED_INSIDE_SHAPE, createCuboidShape(6.0, 8.0, 0.0, 10.0, 6.0, 4.0))
val INVERTED_SOUTH_RAY_TRACE_SHAPE = VoxelShapes.union(INVERTED_INSIDE_SHAPE, createCuboidShape(6.0, 8.0, 12.0, 10.0, 6.0, 16.0))
val INVERTED_WEST_RAY_TRACE_SHAPE = VoxelShapes.union(INVERTED_INSIDE_SHAPE, createCuboidShape(0.0, 8.0, 6.0, 4.0, 6.0, 10.0))
}
override fun appendProperties(builder: StateManager.Builder<Block, BlockState>) {
builder.add(INVERTED_FACING)
}
override fun getOutlineShape(state: BlockState, world: BlockView, pos: BlockPos, entityContext: EntityContext): VoxelShape {
return when (state.get(INVERTED_FACING)) {
Direction.DOWN -> INVERTED_DOWN_SHAPE
Direction.NORTH -> INVERTED_NORTH_SHAPE
Direction.SOUTH -> INVERTED_SOUTH_SHAPE
Direction.WEST -> INVERTED_WEST_SHAPE
Direction.EAST -> INVERTED_EAST_SHAPE
else -> INVERTED_DEFAULT_SHAPE
}
}
override fun getRayTraceShape(state: BlockState, world: BlockView, pos: BlockPos): VoxelShape {
return when (state.get(INVERTED_FACING)) {
Direction.DOWN -> INVERTED_DOWN_RAY_TRACE_SHAPE
Direction.NORTH -> INVERTED_NORTH_RAY_TRACE_SHAPE
Direction.SOUTH -> INVERTED_SOUTH_RAY_TRACE_SHAPE
Direction.WEST -> INVERTED_WEST_RAY_TRACE_SHAPE
Direction.EAST -> INVERTED_EAST_RAY_TRACE_SHAPE
else -> INVERTED_INSIDE_SHAPE
}
}
override fun getPlacementState(context: ItemPlacementContext): BlockState {
val hitFacing = context.side.opposite
val facing = if (hitFacing.axis == Direction.Axis.Y) Direction.UP else hitFacing
return defaultState.with(INVERTED_FACING, facing)
}
override fun createBlockEntity(world: BlockView) = GoldHopperBlockEntity(true)
}

View File

@ -20,69 +20,24 @@ import net.minecraft.util.shape.VoxelShape
import net.minecraft.util.shape.VoxelShapes import net.minecraft.util.shape.VoxelShapes
import net.minecraft.world.BlockView import net.minecraft.world.BlockView
import net.minecraft.world.World import net.minecraft.world.World
import net.shadowfacts.extrahoppers.block.base.BaseHopperBlock
import net.shadowfacts.extrahoppers.block.base.BlockWithEntity import net.shadowfacts.extrahoppers.block.base.BlockWithEntity
/** /**
* @author shadowfacts * @author shadowfacts
*/ */
class WoodHopperBlock: BlockWithEntity<WoodHopperBlockEntity>(FabricBlockSettings.of(Material.WOOD).strength(2f, 3f).sounds(BlockSoundGroup.WOOD).nonOpaque().build()) { class WoodHopperBlock: BaseHopperBlock<WoodHopperBlockEntity>(
FabricBlockSettings.of(Material.WOOD)
.strength(2f, 3f)
.sounds(BlockSoundGroup.WOOD)
.nonOpaque()
.build()
) {
companion object { companion object {
val ID = Identifier("extrahoppers", "wood_hopper") val ID = Identifier("extrahoppers", "wood_hopper")
val FACING = Properties.HOPPER_FACING
// todo: redstone support
// val ENABLED = Properties.ENABLED
val TOP_SHAPE = createCuboidShape(0.0, 10.0, 0.0, 16.0, 16.0, 16.0)
val MIDDLE_SHAPE = createCuboidShape(4.0, 4.0, 4.0, 12.0, 10.0, 12.0)
val OUTSIDE_SHAPE = VoxelShapes.union(MIDDLE_SHAPE, TOP_SHAPE)
val DEFAULT_SHAPE = VoxelShapes.combineAndSimplify(OUTSIDE_SHAPE, Hopper.INSIDE_SHAPE, BooleanBiFunction.ONLY_FIRST)
val DOWN_SHAPE = VoxelShapes.union(DEFAULT_SHAPE, createCuboidShape(6.0, 0.0, 6.0, 10.0, 4.0, 10.0))
val EAST_SHAPE = VoxelShapes.union(DEFAULT_SHAPE, createCuboidShape(12.0, 4.0, 6.0, 16.0, 8.0, 10.0))
val NORTH_SHAPE = VoxelShapes.union(DEFAULT_SHAPE, createCuboidShape(6.0, 4.0, 0.0, 10.0, 8.0, 4.0))
val SOUTH_SHAPE = VoxelShapes.union(DEFAULT_SHAPE, createCuboidShape(6.0, 4.0, 12.0, 10.0, 8.0, 16.0))
val WEST_SHAPE = VoxelShapes.union(DEFAULT_SHAPE, createCuboidShape(0.0, 4.0, 6.0, 4.0, 8.0, 10.0))
val DOWN_RAY_TRACE_SHAPE = Hopper.INSIDE_SHAPE
val EAST_RAY_TRACE_SHAPE = VoxelShapes.union(Hopper.INSIDE_SHAPE, createCuboidShape(12.0, 8.0, 6.0, 16.0, 10.0, 10.0))
val NORTH_RAY_TRACE_SHAPE = VoxelShapes.union(Hopper.INSIDE_SHAPE, createCuboidShape(6.0, 8.0, 0.0, 10.0, 10.0, 4.0))
val SOUTH_RAY_TRACE_SHAPE = VoxelShapes.union(Hopper.INSIDE_SHAPE, createCuboidShape(6.0, 8.0, 12.0, 10.0, 10.0, 16.0))
val WEST_RAY_TRACE_SHAPE = VoxelShapes.union(Hopper.INSIDE_SHAPE, createCuboidShape(0.0, 8.0, 6.0, 4.0, 10.0, 10.0))
} }
override fun appendProperties(builder: StateManager.Builder<Block, BlockState>) { override fun createBlockEntity(world: BlockView) = WoodHopperBlockEntity()
builder.add(FACING)
}
override fun getOutlineShape(state: BlockState, world: BlockView, pos: BlockPos, entityContext: EntityContext): VoxelShape {
return when (state.get(FACING)) {
Direction.DOWN -> DOWN_SHAPE
Direction.NORTH -> NORTH_SHAPE
Direction.SOUTH -> SOUTH_SHAPE
Direction.WEST -> WEST_SHAPE
Direction.EAST -> EAST_SHAPE
else -> DEFAULT_SHAPE
}
}
override fun getRayTraceShape(state: BlockState, world: BlockView, pos: BlockPos): VoxelShape {
return when (state.get(FACING)) {
Direction.DOWN -> DOWN_RAY_TRACE_SHAPE
Direction.NORTH -> NORTH_RAY_TRACE_SHAPE
Direction.SOUTH -> SOUTH_RAY_TRACE_SHAPE
Direction.WEST -> WEST_RAY_TRACE_SHAPE
Direction.EAST -> EAST_RAY_TRACE_SHAPE
else -> Hopper.INSIDE_SHAPE
}
}
override fun getPlacementState(context: ItemPlacementContext): BlockState {
val hitFacing = context.side.opposite
val facing = if (hitFacing.axis == Direction.Axis.Y) Direction.DOWN else hitFacing
return defaultState.with(FACING, facing)
}
override fun createBlockEntity(world: BlockView): WoodHopperBlockEntity {
return WoodHopperBlockEntity()
}
override fun onUse(state: BlockState, world: World, pos: BlockPos, player: PlayerEntity, hand: Hand, hitResult: BlockHitResult): ActionResult { override fun onUse(state: BlockState, world: World, pos: BlockPos, player: PlayerEntity, hand: Hand, hitResult: BlockHitResult): ActionResult {
if (!world.isClient) { if (!world.isClient) {
@ -94,24 +49,10 @@ class WoodHopperBlock: BlockWithEntity<WoodHopperBlockEntity>(FabricBlockSetting
return ActionResult.SUCCESS return ActionResult.SUCCESS
} }
override fun onBlockRemoved(oldState: BlockState, world: World, pos: BlockPos, newState: BlockState, bl: Boolean) {
if (oldState.block != newState.block) {
getBlockEntity(world, pos)?.also {
ItemScatterer.spawn(world, pos, it)
world.updateHorizontalAdjacent(pos, this)
}
super.onBlockRemoved(oldState, world, pos, newState, bl)
}
}
override fun onEntityCollision(state: BlockState, world: World, pos: BlockPos, entity: Entity) { override fun onEntityCollision(state: BlockState, world: World, pos: BlockPos, entity: Entity) {
getBlockEntity(world, pos)?.also { getBlockEntity(world, pos)?.also {
it.onEntityCollision(entity) it.onEntityCollision(entity)
} }
} }
override fun canPlaceAtSide(blockState: BlockState?, blockView: BlockView?, blockPos: BlockPos?, blockPlacementEnvironment: BlockPlacementEnvironment?): Boolean {
return false
}
} }

View File

@ -1,167 +1,19 @@
package net.shadowfacts.extrahoppers.block.wood package net.shadowfacts.extrahoppers.block.wood
import net.minecraft.block.entity.BlockEntity
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.util.BooleanBiFunction
import net.minecraft.util.DefaultedList
import net.minecraft.util.Tickable import net.minecraft.util.Tickable
import net.minecraft.util.math.Direction import net.minecraft.util.math.Direction
import net.minecraft.util.shape.VoxelShapes import net.shadowfacts.extrahoppers.block.base.BaseHopperBlock
import net.shadowfacts.extrahoppers.block.base.BaseHopperBlockEntity
import net.shadowfacts.extrahoppers.init.EHBlockEntities import net.shadowfacts.extrahoppers.init.EHBlockEntities
import net.shadowfacts.extrahoppers.util.toVec3d
class WoodHopperBlockEntity: BlockEntity(EHBlockEntities.WOOD_HOPPER), Inventory, Hopper, Tickable { /**
companion object { * @author shadowfacts
val TRANSFER_COOLDOWN = 40 */
} class WoodHopperBlockEntity: BaseHopperBlockEntity(EHBlockEntities.WOOD_HOPPER), Tickable {
var inventory: DefaultedList<ItemStack> = DefaultedList.ofSize(1, ItemStack.EMPTY) override val inventorySize = 1
private set override val maxTransferCooldown = 40
var transferCooldown = -1 override val facingProp = BaseHopperBlock.FACING
var lastTickTime = 0L override val inputSide = Direction.UP
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
lastTickTime = world.time
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()
}
}
}
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()) || HopperBlockEntity.extract(this))) {
didWork = true
}
if (didWork) {
transferCooldown = TRANSFER_COOLDOWN
markDirty()
}
}
fun insert(): Boolean {
val outputInv = getOutputInventory() ?: return false
val insertionSide = cachedState.get(WoodHopperBlock.FACING).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 isFull(): Boolean {
return inventory.none(ItemStack::isEmpty)
}
fun getOutputInventory(): Inventory? {
val facing = cachedState.get(WoodHopperBlock.FACING)
return HopperBlockEntity.getInventoryAt(world!!, pos.offset(facing))
}
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)
}
// 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()
} }

View File

@ -1,16 +1,19 @@
package net.shadowfacts.extrahoppers.block.wood package net.shadowfacts.extrahoppers.block.wood
import net.minecraft.container.Container
import net.minecraft.container.Slot import net.minecraft.container.Slot
import net.minecraft.entity.player.PlayerEntity import net.minecraft.entity.player.PlayerEntity
import net.minecraft.entity.player.PlayerInventory import net.minecraft.entity.player.PlayerInventory
import net.minecraft.inventory.Inventory import net.minecraft.inventory.Inventory
import net.minecraft.item.ItemStack
import net.minecraft.util.Identifier import net.minecraft.util.Identifier
import net.minecraft.util.PacketByteBuf import net.minecraft.util.PacketByteBuf
import net.shadowfacts.extrahoppers.block.base.BaseHopperContainer
import net.shadowfacts.extrahoppers.init.EHBlocks import net.shadowfacts.extrahoppers.init.EHBlocks
class WoodHopperContainer(syncId: Int, playerInv: PlayerInventory, val hopperInv: Inventory): Container(null, syncId) { class WoodHopperContainer(
syncId: Int,
playerInv: PlayerInventory,
hopper: WoodHopperBlockEntity
): BaseHopperContainer<WoodHopperBlockEntity>(syncId, playerInv, hopper) {
companion object { companion object {
val ID = Identifier("extrahoppers", "wood_hopper") val ID = Identifier("extrahoppers", "wood_hopper")
@ -22,53 +25,8 @@ class WoodHopperContainer(syncId: Int, playerInv: PlayerInventory, val hopperInv
} }
} }
init { override fun addHopperSlots() {
addSlot(Slot(hopperInv, 0, 80, 20)) addSlot(Slot(hopper, 0, 80, 20))
// player inv
for (y in 0 until 3) {
for (x in 0 until 9) {
addSlot(Slot(playerInv, x + y * 9 + 9, 8 + x * 18, 51 + y * 18))
}
}
// hotbar
for (x in 0 until 9) {
addSlot(Slot(playerInv, x, 8 + x * 18, 109))
}
}
override fun canUse(player: PlayerEntity): Boolean {
return true
}
override fun close(player: PlayerEntity) {
super.close(player)
hopperInv.onInvClose(player)
}
override fun transferSlot(player: PlayerEntity, slotIndex: Int): ItemStack {
var remaining = ItemStack.EMPTY
val slot = slots[slotIndex]
if (slot != null && slot.hasStack()) {
val slotStack = slot.stack
remaining = slotStack.copy()
if (slotIndex < hopperInv.invSize) {
if (!insertItem(slotStack, hopperInv.invSize, slots.size, true)) {
return ItemStack.EMPTY;
}
} else if (!insertItem(slotStack, 0, hopperInv.invSize, false)) {
return ItemStack.EMPTY;
}
if (slotStack.isEmpty) {
slot.stack = ItemStack.EMPTY;
} else {
slot.markDirty()
}
}
return remaining
} }
} }

View File

@ -1,37 +1,22 @@
package net.shadowfacts.extrahoppers.block.wood package net.shadowfacts.extrahoppers.block.wood
import com.mojang.blaze3d.platform.GlStateManager
import net.minecraft.client.MinecraftClient import net.minecraft.client.MinecraftClient
import net.minecraft.client.gui.screen.ingame.ContainerScreen
import net.minecraft.entity.player.PlayerInventory import net.minecraft.entity.player.PlayerInventory
import net.minecraft.text.LiteralText import net.minecraft.text.TranslatableText
import net.minecraft.util.Identifier import net.minecraft.util.Identifier
import net.shadowfacts.extrahoppers.block.base.BaseHopperScreen
class WoodHopperScreen(container: WoodHopperContainer, playerInv: PlayerInventory): ContainerScreen<WoodHopperContainer>(container, playerInv, LiteralText("Wooden Hopper")) { class WoodHopperScreen(
container: WoodHopperContainer,
playerInv: PlayerInventory
): BaseHopperScreen<WoodHopperContainer>(container, playerInv, TranslatableText("block.extrahoppers.gold_hopper")) {
companion object { companion object {
val BACKGROUND = Identifier("extrahoppers", "textures/gui/wood_hopper.png")
fun create(container: WoodHopperContainer): WoodHopperScreen { fun create(container: WoodHopperContainer): WoodHopperScreen {
return WoodHopperScreen(container, MinecraftClient.getInstance().player!!.inventory) return WoodHopperScreen(container, MinecraftClient.getInstance().player!!.inventory)
} }
} }
init { override val background = Identifier("extrahoppers", "textures/gui/wood_hopper.png")
containerHeight = 133
}
override fun drawForeground(mouseX: Int, mouseY: Int) {
font.draw(title.asFormattedString(), 8f, 6f, 0x404040)
font.draw(playerInventory.displayName.asFormattedString(), 8f, containerHeight - 94f, 0x404040)
}
override fun drawBackground(f: Float, i: Int, j: Int) {
GlStateManager.color4f(1f, 1f, 1f, 1f)
minecraft!!.textureManager.bindTexture(BACKGROUND)
val x = (width - containerWidth) / 2
val y = (height - containerHeight) / 2
blit(x, y, 0, 0, containerWidth, containerHeight)
}
} }

View File

@ -5,15 +5,23 @@ import net.minecraft.block.entity.BlockEntity
import net.minecraft.block.entity.BlockEntityType import net.minecraft.block.entity.BlockEntityType
import net.minecraft.util.Identifier import net.minecraft.util.Identifier
import net.minecraft.util.registry.Registry import net.minecraft.util.registry.Registry
import net.shadowfacts.extrahoppers.block.gold.GoldHopperBlock
import net.shadowfacts.extrahoppers.block.gold.GoldHopperBlockEntity
import net.shadowfacts.extrahoppers.block.gold.InvertedGoldHopperBlock
import net.shadowfacts.extrahoppers.block.wood.WoodHopperBlock import net.shadowfacts.extrahoppers.block.wood.WoodHopperBlock
import net.shadowfacts.extrahoppers.block.wood.WoodHopperBlockEntity import net.shadowfacts.extrahoppers.block.wood.WoodHopperBlockEntity
object EHBlockEntities { object EHBlockEntities {
val WOOD_HOPPER = create(::WoodHopperBlockEntity, EHBlocks.WOOD_HOPPER) val WOOD_HOPPER = create(::WoodHopperBlockEntity, EHBlocks.WOOD_HOPPER)
val GOLD_HOPPER = create({ GoldHopperBlockEntity(false) }, EHBlocks.GOLD_HOPPER)
val INVERTED_GOLD_HOPPER = create({ GoldHopperBlockEntity(true) }, EHBlocks.INVERTED_GOLD_HOPPER)
// val INVERTED_GOLD_HOPPER = create(::InvertedGoldHopperBlockEntity, EHBlocks.INVERTED_GOLD_HOPPER)
fun init() { fun init() {
register(WoodHopperBlock.ID, WOOD_HOPPER) register(WoodHopperBlock.ID, WOOD_HOPPER)
register(GoldHopperBlock.ID, GOLD_HOPPER)
register(InvertedGoldHopperBlock.ID, INVERTED_GOLD_HOPPER)
} }
private fun <T: BlockEntity> create(builder: () -> T, block: Block): BlockEntityType<T> { private fun <T: BlockEntity> create(builder: () -> T, block: Block): BlockEntityType<T> {
@ -24,4 +32,4 @@ object EHBlockEntities {
Registry.register(Registry.BLOCK_ENTITY_TYPE, id, type) Registry.register(Registry.BLOCK_ENTITY_TYPE, id, type)
} }
} }

View File

@ -3,18 +3,24 @@ package net.shadowfacts.extrahoppers.init
import net.minecraft.block.Block import net.minecraft.block.Block
import net.minecraft.util.Identifier import net.minecraft.util.Identifier
import net.minecraft.util.registry.Registry import net.minecraft.util.registry.Registry
import net.shadowfacts.extrahoppers.block.gold.GoldHopperBlock
import net.shadowfacts.extrahoppers.block.gold.InvertedGoldHopperBlock
import net.shadowfacts.extrahoppers.block.wood.WoodHopperBlock import net.shadowfacts.extrahoppers.block.wood.WoodHopperBlock
object EHBlocks { object EHBlocks {
val WOOD_HOPPER = WoodHopperBlock() val WOOD_HOPPER = WoodHopperBlock()
val GOLD_HOPPER = GoldHopperBlock()
val INVERTED_GOLD_HOPPER = InvertedGoldHopperBlock()
fun init() { fun init() {
register(WoodHopperBlock.ID, WOOD_HOPPER) register(WoodHopperBlock.ID, WOOD_HOPPER)
register(GoldHopperBlock.ID, GOLD_HOPPER)
register(InvertedGoldHopperBlock.ID, INVERTED_GOLD_HOPPER)
} }
private fun register(id: Identifier, block: Block) { private fun register(id: Identifier, block: Block) {
Registry.register(Registry.BLOCK, id, block) Registry.register(Registry.BLOCK, id, block)
} }
} }

View File

@ -4,18 +4,24 @@ import net.minecraft.item.BlockItem
import net.minecraft.item.Item import net.minecraft.item.Item
import net.minecraft.util.Identifier import net.minecraft.util.Identifier
import net.minecraft.util.registry.Registry import net.minecraft.util.registry.Registry
import net.shadowfacts.extrahoppers.block.gold.GoldHopperBlock
import net.shadowfacts.extrahoppers.block.gold.InvertedGoldHopperBlock
import net.shadowfacts.extrahoppers.block.wood.WoodHopperBlock import net.shadowfacts.extrahoppers.block.wood.WoodHopperBlock
object EHItems { object EHItems {
val WOOD_HOPPER = BlockItem(EHBlocks.WOOD_HOPPER, Item.Settings()) val WOOD_HOPPER = BlockItem(EHBlocks.WOOD_HOPPER, Item.Settings())
val GOLD_HOPPER = BlockItem(EHBlocks.GOLD_HOPPER, Item.Settings())
val INVERTED_GOLD_HOPPER = BlockItem(EHBlocks.INVERTED_GOLD_HOPPER, Item.Settings())
fun init() { fun init() {
register(WoodHopperBlock.ID, WOOD_HOPPER) register(WoodHopperBlock.ID, WOOD_HOPPER)
register(GoldHopperBlock.ID, GOLD_HOPPER)
register(InvertedGoldHopperBlock.ID, INVERTED_GOLD_HOPPER)
} }
private fun register(id: Identifier, item: Item) { private fun register(id: Identifier, item: Item) {
Registry.register(Registry.ITEM, id, item) Registry.register(Registry.ITEM, id, item)
} }
} }

View File

@ -0,0 +1,9 @@
{
"variants": {
"facing=down": { "model": "extrahoppers:block/gold_hopper" },
"facing=north": { "model": "extrahoppers:block/gold_hopper_side" },
"facing=south": { "model": "extrahoppers:block/gold_hopper_side", "y": 180 },
"facing=west": { "model": "extrahoppers:block/gold_hopper_side", "y": 270 },
"facing=east": { "model": "extrahoppers:block/gold_hopper_side", "y": 90 }
}
}

View File

@ -0,0 +1,9 @@
{
"variants": {
"inverted_facing=up": { "model": "extrahoppers:block/inverted_gold_hopper" },
"inverted_facing=north": { "model": "extrahoppers:block/gold_hopper_side", "x": 180, "y": 180 },
"inverted_facing=south": { "model": "extrahoppers:block/gold_hopper_side", "x": 180 },
"inverted_facing=west": { "model": "extrahoppers:block/gold_hopper_side", "x": 180, "y": 90 },
"inverted_facing=east": { "model": "extrahoppers:block/gold_hopper_side", "x": 180, "y": 270 }
}
}

View File

@ -1,3 +1,5 @@
{ {
"block.extrahoppers.wood_hopper": "Wooden Hopper" "block.extrahoppers.wood_hopper": "Wooden Hopper",
"block.extrahoppers.gold_hopper": "Golden Hopper",
"block.extrahoppers.inverted_gold_hopper": "Inverted Golden Hopper"
} }

View File

@ -0,0 +1,41 @@
{
"parent": "block/hopper",
"textures": {
"particle": "block/gold_block",
"top": "block/gold_block",
"side": "block/gold_block",
"inside": "block/gold_block"
},
"display": {
"gui": {
"rotation": [ 30, 225, 0 ],
"translation": [ 0, 0, 0],
"scale":[ 0.625, 0.625, 0.625 ]
},
"ground": {
"rotation": [ 0, 0, 0 ],
"translation": [ 0, 3, 0],
"scale":[ 0.25, 0.25, 0.25 ]
},
"fixed": {
"rotation": [ 0, 0, 0 ],
"translation": [ 0, 0, 0],
"scale":[ 0.5, 0.5, 0.5 ]
},
"thirdperson_righthand": {
"rotation": [ 75, 45, 0 ],
"translation": [ 0, 2.5, 0],
"scale": [ 0.375, 0.375, 0.375 ]
},
"firstperson_righthand": {
"rotation": [ 0, 45, 0 ],
"translation": [ 0, 0, 0 ],
"scale": [ 0.40, 0.40, 0.40 ]
},
"firstperson_lefthand": {
"rotation": [ 0, 225, 0 ],
"translation": [ 0, 0, 0 ],
"scale": [ 0.40, 0.40, 0.40 ]
}
}
}

View File

@ -0,0 +1,9 @@
{
"parent": "block/hopper_side",
"textures": {
"particle": "block/gold_block",
"top": "block/gold_block",
"side": "block/gold_block",
"inside": "block/gold_block"
}
}

View File

@ -0,0 +1,127 @@
{
"ambientocclusion": false,
"textures": {
"particle": "block/gold_block",
"top": "block/gold_block",
"side": "block/gold_block",
"inside": "block/gold_block"
},
"elements": [
{
"from": [ 0, 5, 0 ],
"to": [ 16, 6, 16 ],
"faces": {
"down": { "texture": "#inside" },
"up": { "texture": "#side" },
"north": { "texture": "#side" },
"south": { "texture": "#side" },
"west": { "texture": "#side" },
"east": { "texture": "#side" }
}
},
{
"from": [ 0, 0, 0 ],
"to": [ 2, 5, 16 ],
"faces": {
"down": { "texture": "#top" },
"up": { "texture": "#side" },
"north": { "texture": "#side" },
"south": { "texture": "#side" },
"west": { "texture": "#side" },
"east": { "texture": "#side" }
}
},
{
"from": [ 14, 0, 0 ],
"to": [ 16, 5, 16 ],
"faces": {
"down": { "texture": "#top" },
"up": { "texture": "#side" },
"north": { "texture": "#side" },
"south": { "texture": "#side" },
"west": { "texture": "#side" },
"east": { "texture": "#side" }
}
},
{
"from": [ 2, 0, 0 ],
"to": [ 14, 5, 2 ],
"faces": {
"down": { "texture": "#top" },
"up": { "texture": "#side" },
"north": { "texture": "#side" },
"south": { "texture": "#side" },
"west": { "texture": "#side" },
"east": { "texture": "#side" }
}
},
{
"from": [ 2, 0, 14 ],
"to": [ 14, 5, 16 ],
"faces": {
"down": { "texture": "#top" },
"up": { "texture": "#side" },
"north": { "texture": "#side" },
"south": { "texture": "#side" },
"west": { "texture": "#side" },
"east": { "texture": "#side" }
}
},
{
"from": [ 4, 6, 4 ],
"to": [ 12, 12, 12 ],
"faces": {
"down": { "texture": "#side" },
"up": { "texture": "#side" },
"north": { "texture": "#side" },
"south": { "texture": "#side" },
"west": { "texture": "#side" },
"east": { "texture": "#side" }
}
},
{
"from": [ 6, 12, 6 ],
"to": [ 10, 16, 10 ],
"faces": {
"down": { "texture": "#side" },
"up": { "texture": "#side" },
"north": { "texture": "#side" },
"south": { "texture": "#side" },
"west": { "texture": "#side" },
"east": { "texture": "#side" }
}
}
],
"display": {
"gui": {
"rotation": [ 30, 225, 0 ],
"translation": [ 0, 0, 0],
"scale":[ 0.625, 0.625, 0.625 ]
},
"ground": {
"rotation": [ 0, 0, 0 ],
"translation": [ 0, 3, 0],
"scale":[ 0.25, 0.25, 0.25 ]
},
"fixed": {
"rotation": [ 0, 0, 0 ],
"translation": [ 0, 0, 0],
"scale":[ 0.5, 0.5, 0.5 ]
},
"thirdperson_righthand": {
"rotation": [ 75, 45, 0 ],
"translation": [ 0, 2.5, 0],
"scale": [ 0.375, 0.375, 0.375 ]
},
"firstperson_righthand": {
"rotation": [ 0, 45, 0 ],
"translation": [ 0, 0, 0 ],
"scale": [ 0.40, 0.40, 0.40 ]
},
"firstperson_lefthand": {
"rotation": [ 0, 225, 0 ],
"translation": [ 0, 0, 0 ],
"scale": [ 0.40, 0.40, 0.40 ]
}
}
}

View File

@ -0,0 +1,3 @@
{
"parent": "extrahoppers:block/gold_hopper"
}

View File

@ -0,0 +1,3 @@
{
"parent": "extrahoppers:block/inverted_gold_hopper"
}

View File

@ -0,0 +1,35 @@
{
"parent": "minecraft:recipes/root",
"rewards": {
"recipes": [
"extrahoppers:gold_hopper"
]
},
"criteria": {
"has_hopper_and_gold": {
"trigger": "minecraft:inventory_changed",
"conditions": {
"items": [
{
"item": "minecraft:hopper"
},
{
"item": "minecraft:gold_ingot"
}
]
}
},
"has_the_recipe": {
"trigger": "minecraft:recipe_unlocked",
"conditions": {
"recipe": "extrahoppers:gold_hopper"
}
}
},
"requirements": [
[
"has_hopper_and_gold",
"has_the_recipe"
]
]
}

View File

@ -0,0 +1,19 @@
{
"type": "minecraft:block",
"pools": [
{
"rolls": 1,
"entries": [
{
"type": "minecraft:item",
"name": "extrahoppers:gold_hopper"
}
],
"conditions": [
{
"condition": "minecraft:survives_explosion"
}
]
}
]
}

View File

@ -0,0 +1,19 @@
{
"type": "minecraft:crafting_shaped",
"pattern": [
"G G",
"GCG",
" G "
],
"key": {
"C": {
"item": "minecraft:chest"
},
"G": {
"item": "minecraft:gold_ingot"
}
},
"result": {
"item": "extrahoppers:gold_hopper"
}
}

View File

@ -0,0 +1,11 @@
{
"type": "minecraft:crafting_shapeless",
"ingredients": [
{
"item": "extrahoppers:gold_hopper"
}
],
"result": {
"item": "extrahoppers:inverted_gold_hopper"
}
}

View File

@ -0,0 +1,11 @@
{
"type": "minecraft:crafting_shapeless",
"ingredients": [
{
"item": "extrahoppers:inverted_gold_hopper"
}
],
"result": {
"item": "extrahoppers:gold_hopper"
}
}