11package io.github.lucaargolo.kibe.block
22
33import com.mojang.serialization.MapCodec
4- import io.github.lucaargolo.kibe.blockentity.BlockEntityCompendium
54import io.github.lucaargolo.kibe.blockentity.DrawbridgeBlockEntity
65import io.github.lucaargolo.kibe.menu.DrawbridgeScreenHandler
76import io.github.lucaargolo.kibe.utils.menu.BlockScreenHandlerFactory
7+ import net.fabricmc.fabric.api.entity.FakePlayer
88import net.minecraft.block.*
9- import net.minecraft.block.entity.BlockEntity
10- import net.minecraft.block.entity.BlockEntityTicker
11- import net.minecraft.block.entity.BlockEntityType
9+ import net.minecraft.enchantment.Enchantments
1210import net.minecraft.entity.player.PlayerEntity
1311import net.minecraft.inventory.Inventory
14- import net.minecraft.item.ItemPlacementContext
15- import net.minecraft.item.ItemStack
12+ import net.minecraft.item.*
13+ import net.minecraft.registry.RegistryKeys
14+ import net.minecraft.registry.tag.BlockTags
15+ import net.minecraft.screen.ScreenHandler
16+ import net.minecraft.server.world.ServerWorld
17+ import net.minecraft.sound.SoundCategory
18+ import net.minecraft.sound.SoundEvents
1619import net.minecraft.state.StateManager
1720import net.minecraft.state.property.Properties
18- import net.minecraft.util.ActionResult
19- import net.minecraft.util.BlockMirror
20- import net.minecraft.util.BlockRotation
21- import net.minecraft.util.ItemScatterer
21+ import net.minecraft.util.*
2222import net.minecraft.util.hit.BlockHitResult
2323import net.minecraft.util.math.BlockPos
24- import net.minecraft.util.math.MathHelper
24+ import net.minecraft.util.math.Direction
25+ import net.minecraft.util.math.Vec3d
26+ import net.minecraft.util.math.random.Random
2527import net.minecraft.world.World
2628
2729class Drawbridge (settings : Settings ): BlockWithEntity(settings) {
2830
29- override fun createBlockEntity (blockPos : BlockPos , blockState : BlockState ): BlockEntity {
30- return DrawbridgeBlockEntity (blockPos, blockState)
31- }
32-
33- override fun <T : BlockEntity ?> getTicker (world : World ? , state : BlockState ? , blockEntityType : BlockEntityType <T >? ): BlockEntityTicker <T >? {
34- return validateTicker(blockEntityType, BlockEntityCompendium .DRAWBRIDGE , DrawbridgeBlockEntity ::tick)
31+ init {
32+ defaultState = stateManager.defaultState.with (Properties .FACING , Direction .NORTH ).with (Properties .TRIGGERED , false )
3533 }
3634
3735 override fun appendProperties (stateManager : StateManager .Builder <Block ?, BlockState ?>) {
3836 stateManager.add(Properties .FACING )
37+ stateManager.add(Properties .TRIGGERED )
38+ }
39+
40+ override fun getPlacementState (ctx : ItemPlacementContext ): BlockState ? {
41+ return defaultState.with (Properties .FACING , ctx.playerLookDirection.opposite)
3942 }
4043
4144 override fun rotate (state : BlockState , rotation : BlockRotation ): BlockState {
@@ -46,21 +49,58 @@ class Drawbridge(settings: Settings): BlockWithEntity(settings) {
4649 return state.rotate(mirror.getRotation(state[DispenserBlock .FACING ]))
4750 }
4851
49- override fun hasComparatorOutput (state : BlockState ? ) = true
52+ override fun neighborUpdate (state : BlockState , world : World , pos : BlockPos , block : Block ? , fromPos : BlockPos ? , notify : Boolean ) {
53+ val isReceivingPower = world.isReceivingRedstonePower(pos) || world.isReceivingRedstonePower(pos.up())
54+ val triggered = state[DispenserBlock .TRIGGERED ]
55+ if (isReceivingPower && ! triggered) {
56+ world.scheduleBlockTick(pos, this , 8 )
57+ world.setBlockState(pos, state.with (DispenserBlock .TRIGGERED , true ), 4 )
58+ } else if (! isReceivingPower && triggered) {
59+ world.scheduleBlockTick(pos, this , 8 )
60+ world.setBlockState(pos, state.with (DispenserBlock .TRIGGERED , false ), 4 )
61+ }
62+ }
5063
51- override fun getComparatorOutput (state : BlockState , world : World , pos : BlockPos ): Int {
52- var output = 0
53- (world.getBlockEntity(pos) as ? DrawbridgeBlockEntity )?.let { entity ->
54- var i = 0
55- var f = 0.0f
56- val itemStack: ItemStack = entity.getStack(0 )
57- if (! itemStack.isEmpty) {
58- f + = itemStack.count.toFloat() / entity.maxCountPerStack.coerceAtMost(itemStack.maxCount).toFloat()
59- ++ i
64+ override fun scheduledTick (state : BlockState , world : ServerWorld , pos : BlockPos , random : Random ) {
65+ val facing = state[Properties .FACING ]
66+ val triggered = state[DispenserBlock .TRIGGERED ]
67+ (world.getBlockEntity(pos) as ? DrawbridgeBlockEntity )?.let {
68+ if (triggered) {
69+ val stack = it.getStack(0 )
70+ val front = world.getBlockState(pos.offset(facing))
71+ if (front.isAir || isSameBlock(stack, world, pos.offset(facing), front)) {
72+ val distance = sameBlockDistance(stack, world, pos, facing)
73+ if (! stack.isEmpty && distance < stack.maxCount) {
74+ placeBlock(stack, world, pos.offset(facing, distance + 1 ), facing)
75+ world.playSound(null , pos, SoundEvents .BLOCK_PISTON_EXTEND , SoundCategory .BLOCKS , 1f , world.random.nextFloat() * 0.25f + 0.6f )
76+ world.scheduleBlockTick(pos, this , 8 )
77+ }
78+ }
79+ }else {
80+ val stack = it.getStack(0 ).let { stack ->
81+ if (stack.isEmpty) {
82+ val blockStack = stackFromBlock(world, pos.offset(facing))
83+ return @let blockStack
84+ } else {
85+ return @let stack
86+ }
87+ }
88+ val distance = sameBlockDistance(stack, world, pos, facing)
89+ if (distance > 0 && ! stack.isEmpty && stack.count < stack.maxCount) {
90+ breakBlock(stack, world, pos.offset(facing, distance), it)
91+ world.playSound(null , pos, SoundEvents .BLOCK_PISTON_CONTRACT , SoundCategory .BLOCKS , 1f , world.random.nextFloat() * 0.25f + 0.6f )
92+ world.scheduleBlockTick(pos, this , 8 )
93+ }
6094 }
61- output = MathHelper .floor(f * 14.0f ) + if (i > 0 ) 1 else 0
6295 }
63- return output
96+ }
97+
98+ override fun createBlockEntity (pos : BlockPos , state : BlockState ) = DrawbridgeBlockEntity (pos, state)
99+
100+ override fun hasComparatorOutput (state : BlockState ? ) = true
101+
102+ override fun getComparatorOutput (state : BlockState , world : World , pos : BlockPos ): Int {
103+ return ScreenHandler .calculateComparatorOutput(world.getBlockEntity(pos))
64104 }
65105
66106 override fun onStateReplaced (state : BlockState , world : World , pos : BlockPos ? , newState : BlockState , notify : Boolean ) {
@@ -73,21 +113,82 @@ class Drawbridge(settings: Settings): BlockWithEntity(settings) {
73113 }
74114 }
75115
76- override fun getPlacementState (ctx : ItemPlacementContext ): BlockState ? {
77- return defaultState.with (Properties .FACING , ctx.playerLookDirection.opposite)
78- }
79-
80116 override fun onUse (state : BlockState ? , world : World , pos : BlockPos , player : PlayerEntity , hit : BlockHitResult ? ): ActionResult {
81117 player.openHandledScreen(BlockScreenHandlerFactory (this , pos, ::DrawbridgeScreenHandler ))
82118 return ActionResult .SUCCESS
83119 }
84120
85121 override fun getRenderType (state : BlockState ? ) = BlockRenderType .MODEL
86122
87- override fun getCodec (): MapCodec <Drawbridge > = CODEC
123+ override fun getCodec (): MapCodec <Placer > = CODEC
88124
89125 companion object {
90- private val CODEC : MapCodec <Drawbridge > = createCodec(::Drawbridge )
126+ private val CODEC : MapCodec <Placer > = createCodec(::Placer )
127+
128+ private fun placeBlock (stack : ItemStack , world : ServerWorld , pos : BlockPos , facing : Direction ) {
129+ val fakePlayer = FakePlayer .get(world)
130+ fakePlayer.setStackInHand(Hand .MAIN_HAND , stack)
131+ val fakeHitPos = Vec3d (pos.x + 0.5 , pos.y + 0.0 , pos.z + 0.5 )
132+ (stack.item as ? BlockItem )?.useOnBlock(ItemUsageContext (fakePlayer, Hand .MAIN_HAND , BlockHitResult (fakeHitPos, facing.opposite, pos, false )))
133+ }
134+
135+ private fun breakBlock (stack : ItemStack , world : World , pos : BlockPos , blockEntity : DrawbridgeBlockEntity ) {
136+ world.breakBlock(pos, false )
137+ if (stack == blockEntity.getStack(0 )) {
138+ stack.increment(1 )
139+ }else {
140+ blockEntity.setStack(0 , stack.copy())
141+ }
142+ }
143+
144+ private fun sameBlockDistance (stack : ItemStack , world : ServerWorld , pos : BlockPos , facing : Direction ): Int {
145+ var distance = 0
146+ while (distance < stack.maxCount && isSameBlock(stack, world, pos.offset(facing, distance+ 1 ))) {
147+ distance++
148+ }
149+ return distance
150+ }
151+
152+ private fun isSameBlock (stack : ItemStack , world : ServerWorld , pos : BlockPos ): Boolean {
153+ return ItemStack .areItemsAndComponentsEqual(stack, stackFromBlock(world, pos))
154+ }
155+
156+ private fun isSameBlock (stack : ItemStack , world : ServerWorld , pos : BlockPos , state : BlockState ): Boolean {
157+ return ItemStack .areItemsAndComponentsEqual(stack, stackFromBlock(world, pos, state))
158+ }
159+
160+ private fun stackFromBlock (world : ServerWorld , pos : BlockPos ): ItemStack {
161+ val state = world.getBlockState(pos)
162+ return stackFromBlock(world, pos, state)
163+ }
164+
165+ private fun stackFromBlock (world : ServerWorld , pos : BlockPos , state : BlockState ): ItemStack {
166+ val entity = world.getBlockEntity(pos)
167+ val list = getDroppedStacks(state, world, pos, entity, null , silkTouchStack(world, state))
168+ if (list.size == 1 ) {
169+ val stack = list.first()
170+ if (stack.count == 1 && stack.item is BlockItem ) {
171+ return stack
172+ }
173+ }
174+ return ItemStack .EMPTY
175+ }
176+
177+ private fun silkTouchStack (world : World , state : BlockState ): ItemStack {
178+ val item = Items .STICK
179+ state.streamTags().forEach {
180+ when (it) {
181+ BlockTags .SHOVEL_MINEABLE -> Items .NETHERITE_SHOVEL
182+ BlockTags .AXE_MINEABLE -> Items .NETHERITE_AXE
183+ BlockTags .PICKAXE_MINEABLE -> Items .NETHERITE_PICKAXE
184+ BlockTags .HOE_MINEABLE -> Items .NETHERITE_HOE
185+ }
186+ }
187+ return item.defaultStack.also {
188+ it.addEnchantment(world.registryManager.get(RegistryKeys .ENCHANTMENT ).getEntry(Enchantments .SILK_TOUCH ).get(), 1 )
189+ }
190+ }
191+
91192 }
92193
93194}
0 commit comments