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.fabric.api.container.ContainerFactory
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.init.EHBlockEntities
import net.shadowfacts.extrahoppers.init.EHBlocks
@ -16,6 +17,7 @@ object ExtraHoppers: ModInitializer {
EHItems.init()
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.fabric.api.client.screen.ContainerScreenFactory
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.WoodHopperScreen
@ -10,6 +12,7 @@ object ExtraHoppersClient: ClientModInitializer {
override fun onInitializeClient() {
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.world.BlockView
import net.minecraft.world.World
import net.shadowfacts.extrahoppers.block.base.BaseHopperBlock
import net.shadowfacts.extrahoppers.block.base.BlockWithEntity
/**
* @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 {
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>) {
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 createBlockEntity(world: BlockView) = WoodHopperBlockEntity()
override fun onUse(state: BlockState, world: World, pos: BlockPos, player: PlayerEntity, hand: Hand, hitResult: BlockHitResult): ActionResult {
if (!world.isClient) {
@ -94,24 +49,10 @@ class WoodHopperBlock: BlockWithEntity<WoodHopperBlockEntity>(FabricBlockSetting
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) {
getBlockEntity(world, pos)?.also {
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
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.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.util.toVec3d
class WoodHopperBlockEntity: BlockEntity(EHBlockEntities.WOOD_HOPPER), Inventory, Hopper, Tickable {
companion object {
val TRANSFER_COOLDOWN = 40
}
/**
* @author shadowfacts
*/
class WoodHopperBlockEntity: BaseHopperBlockEntity(EHBlockEntities.WOOD_HOPPER), Tickable {
var inventory: DefaultedList<ItemStack> = DefaultedList.ofSize(1, ItemStack.EMPTY)
private set
var transferCooldown = -1
var lastTickTime = 0L
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()
override val inventorySize = 1
override val maxTransferCooldown = 40
override val facingProp = BaseHopperBlock.FACING
override val inputSide = Direction.UP
}

View File

@ -1,16 +1,19 @@
package net.shadowfacts.extrahoppers.block.wood
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.inventory.Inventory
import net.minecraft.item.ItemStack
import net.minecraft.util.Identifier
import net.minecraft.util.PacketByteBuf
import net.shadowfacts.extrahoppers.block.base.BaseHopperContainer
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 {
val ID = Identifier("extrahoppers", "wood_hopper")
@ -22,53 +25,8 @@ class WoodHopperContainer(syncId: Int, playerInv: PlayerInventory, val hopperInv
}
}
init {
addSlot(Slot(hopperInv, 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
override fun addHopperSlots() {
addSlot(Slot(hopper, 0, 80, 20))
}
}

View File

@ -1,37 +1,22 @@
package net.shadowfacts.extrahoppers.block.wood
import com.mojang.blaze3d.platform.GlStateManager
import net.minecraft.client.MinecraftClient
import net.minecraft.client.gui.screen.ingame.ContainerScreen
import net.minecraft.entity.player.PlayerInventory
import net.minecraft.text.LiteralText
import net.minecraft.text.TranslatableText
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 {
val BACKGROUND = Identifier("extrahoppers", "textures/gui/wood_hopper.png")
fun create(container: WoodHopperContainer): WoodHopperScreen {
return WoodHopperScreen(container, MinecraftClient.getInstance().player!!.inventory)
}
}
init {
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)
}
override val background = Identifier("extrahoppers", "textures/gui/wood_hopper.png")
}

View File

@ -5,15 +5,23 @@ import net.minecraft.block.entity.BlockEntity
import net.minecraft.block.entity.BlockEntityType
import net.minecraft.util.Identifier
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.WoodHopperBlockEntity
object EHBlockEntities {
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() {
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> {
@ -24,4 +32,4 @@ object EHBlockEntities {
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.util.Identifier
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
object EHBlocks {
val WOOD_HOPPER = WoodHopperBlock()
val GOLD_HOPPER = GoldHopperBlock()
val INVERTED_GOLD_HOPPER = InvertedGoldHopperBlock()
fun init() {
register(WoodHopperBlock.ID, WOOD_HOPPER)
register(GoldHopperBlock.ID, GOLD_HOPPER)
register(InvertedGoldHopperBlock.ID, INVERTED_GOLD_HOPPER)
}
private fun register(id: Identifier, block: 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.util.Identifier
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
object EHItems {
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() {
register(WoodHopperBlock.ID, WOOD_HOPPER)
register(GoldHopperBlock.ID, GOLD_HOPPER)
register(InvertedGoldHopperBlock.ID, INVERTED_GOLD_HOPPER)
}
private fun register(id: Identifier, item: 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"
}
}