package net.minecraft.world.level.block.state;

import java.util.Arrays;
import net.minecraft.world.level.EmptyBlockGetter;
import java.util.function.Predicate;
import net.minecraft.world.phys.Vec3;
import com.mojang.serialization.MapCodec;
import net.minecraft.world.level.block.state.properties.Property;
import com.google.common.collect.ImmutableMap;
import net.minecraft.world.item.DyeColor;
import net.minecraft.world.entity.EntityType;
import java.util.function.ToIntFunction;
import java.util.function.Function;
import net.minecraft.world.level.material.MaterialColor;
import net.minecraft.world.item.Item;
import net.minecraft.world.entity.projectile.Projectile;
import net.minecraft.core.Registry;
import net.minecraft.world.level.block.EntityBlock;
import net.minecraft.world.entity.Entity;
import java.util.Random;
import net.minecraft.world.level.LevelReader;
import net.minecraft.world.MenuProvider;
import net.minecraft.world.phys.shapes.Shapes;
import net.minecraft.world.phys.shapes.CollisionContext;
import net.minecraft.world.phys.shapes.VoxelShape;
import net.minecraft.core.Vec3i;
import net.minecraft.util.Mth;
import net.minecraft.world.level.storage.loot.LootTable;
import net.minecraft.server.level.ServerLevel;
import net.minecraft.world.level.storage.loot.parameters.LootContextParamSets;
import net.minecraft.world.level.storage.loot.parameters.LootContextParams;
import java.util.Collections;
import net.minecraft.world.level.storage.loot.BuiltInLootTables;
import net.minecraft.world.item.ItemStack;
import java.util.List;
import net.minecraft.world.level.storage.loot.LootContext;
import net.minecraft.world.item.BlockPlaceContext;
import net.minecraft.world.level.block.Mirror;
import net.minecraft.world.level.block.Rotation;
import net.minecraft.world.level.material.Fluids;
import net.minecraft.world.level.material.FluidState;
import net.minecraft.world.level.material.PushReaction;
import net.minecraft.world.level.block.RenderShape;
import net.minecraft.world.InteractionResult;
import net.minecraft.world.phys.BlockHitResult;
import net.minecraft.world.InteractionHand;
import net.minecraft.world.entity.player.Player;
import net.minecraft.network.protocol.game.DebugPackets;
import net.minecraft.world.level.block.Block;
import net.minecraft.world.level.Level;
import net.minecraft.world.level.material.Fluid;
import net.minecraft.tags.Tag;
import net.minecraft.tags.FluidTags;
import net.minecraft.world.level.pathfinder.PathComputationType;
import net.minecraft.world.level.BlockGetter;
import net.minecraft.core.BlockPos;
import net.minecraft.world.level.LevelAccessor;
import javax.annotation.Nullable;
import net.minecraft.resources.ResourceLocation;
import net.minecraft.world.level.block.SoundType;
import net.minecraft.world.level.material.Material;
import net.minecraft.core.Direction;

public abstract class BlockBehaviour {
    protected static final Direction[] UPDATE_SHAPE_ORDER;
    protected final Material material;
    protected final boolean hasCollision;
    protected final float explosionResistance;
    protected final boolean isRandomlyTicking;
    protected final SoundType soundType;
    protected final float friction;
    protected final float speedFactor;
    protected final float jumpFactor;
    protected final boolean dynamicShape;
    protected final Properties properties;
    @Nullable
    protected ResourceLocation drops;
    
    public BlockBehaviour(final Properties c) {
        this.material = c.material;
        this.hasCollision = c.hasCollision;
        this.drops = c.drops;
        this.explosionResistance = c.explosionResistance;
        this.isRandomlyTicking = c.isRandomlyTicking;
        this.soundType = c.soundType;
        this.friction = c.friction;
        this.speedFactor = c.speedFactor;
        this.jumpFactor = c.jumpFactor;
        this.dynamicShape = c.dynamicShape;
        this.properties = c;
    }
    
    @Deprecated
    public void updateIndirectNeighbourShapes(final BlockState cfj, final LevelAccessor bqc, final BlockPos fu, final int integer4, final int integer5) {
    }
    
    @Deprecated
    public boolean isPathfindable(final BlockState cfj, final BlockGetter bpg, final BlockPos fu, final PathComputationType czg) {
        switch (czg) {
            case LAND: {
                return !cfj.isCollisionShapeFullBlock(bpg, fu);
            }
            case WATER: {
                return bpg.getFluidState(fu).is(FluidTags.WATER);
            }
            case AIR: {
                return !cfj.isCollisionShapeFullBlock(bpg, fu);
            }
            default: {
                return false;
            }
        }
    }
    
    @Deprecated
    public BlockState updateShape(final BlockState cfj1, final Direction fz, final BlockState cfj3, final LevelAccessor bqc, final BlockPos fu5, final BlockPos fu6) {
        return cfj1;
    }
    
    @Deprecated
    public boolean skipRendering(final BlockState cfj1, final BlockState cfj2, final Direction fz) {
        return false;
    }
    
    @Deprecated
    public void neighborChanged(final BlockState cfj, final Level bqb, final BlockPos fu3, final Block bvr, final BlockPos fu5, final boolean boolean6) {
        DebugPackets.sendNeighborsUpdatePacket(bqb, fu3);
    }
    
    @Deprecated
    public void onPlace(final BlockState cfj1, final Level bqb, final BlockPos fu, final BlockState cfj4, final boolean boolean5) {
    }
    
    @Deprecated
    public void onRemove(final BlockState cfj1, final Level bqb, final BlockPos fu, final BlockState cfj4, final boolean boolean5) {
        if (this.isEntityBlock() && !cfj1.is(cfj4.getBlock())) {
            bqb.removeBlockEntity(fu);
        }
    }
    
    @Deprecated
    public InteractionResult use(final BlockState cfj, final Level bqb, final BlockPos fu, final Player bec, final InteractionHand anf, final BlockHitResult deh) {
        return InteractionResult.PASS;
    }
    
    @Deprecated
    public boolean triggerEvent(final BlockState cfj, final Level bqb, final BlockPos fu, final int integer4, final int integer5) {
        return false;
    }
    
    @Deprecated
    public RenderShape getRenderShape(final BlockState cfj) {
        return RenderShape.MODEL;
    }
    
    @Deprecated
    public boolean useShapeForLightOcclusion(final BlockState cfj) {
        return false;
    }
    
    @Deprecated
    public boolean isSignalSource(final BlockState cfj) {
        return false;
    }
    
    @Deprecated
    public PushReaction getPistonPushReaction(final BlockState cfj) {
        return this.material.getPushReaction();
    }
    
    @Deprecated
    public FluidState getFluidState(final BlockState cfj) {
        return Fluids.EMPTY.defaultFluidState();
    }
    
    @Deprecated
    public boolean hasAnalogOutputSignal(final BlockState cfj) {
        return false;
    }
    
    public OffsetType getOffsetType() {
        return OffsetType.NONE;
    }
    
    @Deprecated
    public BlockState rotate(final BlockState cfj, final Rotation cap) {
        return cfj;
    }
    
    @Deprecated
    public BlockState mirror(final BlockState cfj, final Mirror bzj) {
        return cfj;
    }
    
    @Deprecated
    public boolean canBeReplaced(final BlockState cfj, final BlockPlaceContext bin) {
        return this.material.isReplaceable() && (bin.getItemInHand().isEmpty() || bin.getItemInHand().getItem() != this.asItem());
    }
    
    @Deprecated
    public boolean canBeReplaced(final BlockState cfj, final Fluid cwz) {
        return this.material.isReplaceable() || !this.material.isSolid();
    }
    
    @Deprecated
    public List<ItemStack> getDrops(final BlockState cfj, final LootContext.Builder a) {
        final ResourceLocation uh4 = this.getLootTable();
        if (uh4 == BuiltInLootTables.EMPTY) {
            return Collections.<ItemStack>emptyList();
        }
        final LootContext dat5 = a.<BlockState>withParameter(LootContextParams.BLOCK_STATE, cfj).create(LootContextParamSets.BLOCK);
        final ServerLevel zd6 = dat5.getLevel();
        final LootTable daw7 = zd6.getServer().getLootTables().get(uh4);
        return daw7.getRandomItems(dat5);
    }
    
    @Deprecated
    public long getSeed(final BlockState cfj, final BlockPos fu) {
        return Mth.getSeed(fu);
    }
    
    @Deprecated
    public VoxelShape getOcclusionShape(final BlockState cfj, final BlockGetter bpg, final BlockPos fu) {
        return cfj.getShape(bpg, fu);
    }
    
    @Deprecated
    public VoxelShape getBlockSupportShape(final BlockState cfj, final BlockGetter bpg, final BlockPos fu) {
        return this.getCollisionShape(cfj, bpg, fu, CollisionContext.empty());
    }
    
    @Deprecated
    public VoxelShape getInteractionShape(final BlockState cfj, final BlockGetter bpg, final BlockPos fu) {
        return Shapes.empty();
    }
    
    @Deprecated
    public int getLightBlock(final BlockState cfj, final BlockGetter bpg, final BlockPos fu) {
        if (cfj.isSolidRender(bpg, fu)) {
            return bpg.getMaxLightLevel();
        }
        return cfj.propagatesSkylightDown(bpg, fu) ? 0 : 1;
    }
    
    @Nullable
    @Deprecated
    public MenuProvider getMenuProvider(final BlockState cfj, final Level bqb, final BlockPos fu) {
        return null;
    }
    
    @Deprecated
    public boolean canSurvive(final BlockState cfj, final LevelReader bqd, final BlockPos fu) {
        return true;
    }
    
    @Deprecated
    public float getShadeBrightness(final BlockState cfj, final BlockGetter bpg, final BlockPos fu) {
        return cfj.isCollisionShapeFullBlock(bpg, fu) ? 0.2f : 1.0f;
    }
    
    @Deprecated
    public int getAnalogOutputSignal(final BlockState cfj, final Level bqb, final BlockPos fu) {
        return 0;
    }
    
    @Deprecated
    public VoxelShape getShape(final BlockState cfj, final BlockGetter bpg, final BlockPos fu, final CollisionContext der) {
        return Shapes.block();
    }
    
    @Deprecated
    public VoxelShape getCollisionShape(final BlockState cfj, final BlockGetter bpg, final BlockPos fu, final CollisionContext der) {
        return this.hasCollision ? cfj.getShape(bpg, fu) : Shapes.empty();
    }
    
    @Deprecated
    public VoxelShape getVisualShape(final BlockState cfj, final BlockGetter bpg, final BlockPos fu, final CollisionContext der) {
        return this.getCollisionShape(cfj, bpg, fu, der);
    }
    
    @Deprecated
    public void randomTick(final BlockState cfj, final ServerLevel zd, final BlockPos fu, final Random random) {
        this.tick(cfj, zd, fu, random);
    }
    
    @Deprecated
    public void tick(final BlockState cfj, final ServerLevel zd, final BlockPos fu, final Random random) {
    }
    
    @Deprecated
    public float getDestroyProgress(final BlockState cfj, final Player bec, final BlockGetter bpg, final BlockPos fu) {
        final float float6 = cfj.getDestroySpeed(bpg, fu);
        if (float6 == -1.0f) {
            return 0.0f;
        }
        final int integer7 = bec.hasCorrectToolForDrops(cfj) ? 30 : 100;
        return bec.getDestroySpeed(cfj) / float6 / integer7;
    }
    
    @Deprecated
    public void spawnAfterBreak(final BlockState cfj, final Level bqb, final BlockPos fu, final ItemStack bki) {
    }
    
    @Deprecated
    public void attack(final BlockState cfj, final Level bqb, final BlockPos fu, final Player bec) {
    }
    
    @Deprecated
    public int getSignal(final BlockState cfj, final BlockGetter bpg, final BlockPos fu, final Direction fz) {
        return 0;
    }
    
    @Deprecated
    public void entityInside(final BlockState cfj, final Level bqb, final BlockPos fu, final Entity aom) {
    }
    
    @Deprecated
    public int getDirectSignal(final BlockState cfj, final BlockGetter bpg, final BlockPos fu, final Direction fz) {
        return 0;
    }
    
    public final boolean isEntityBlock() {
        return this instanceof EntityBlock;
    }
    
    public final ResourceLocation getLootTable() {
        if (this.drops == null) {
            final ResourceLocation uh2 = Registry.BLOCK.getKey(this.asBlock());
            this.drops = new ResourceLocation(uh2.getNamespace(), "blocks/" + uh2.getPath());
        }
        return this.drops;
    }
    
    @Deprecated
    public void onProjectileHit(final Level bqb, final BlockState cfj, final BlockHitResult deh, final Projectile bes) {
    }
    
    public abstract Item asItem();
    
    protected abstract Block asBlock();
    
    public MaterialColor defaultMaterialColor() {
        return this.properties.materialColor.apply(this.asBlock().defaultBlockState());
    }
    
    static {
        UPDATE_SHAPE_ORDER = new Direction[] { Direction.WEST, Direction.EAST, Direction.NORTH, Direction.SOUTH, Direction.DOWN, Direction.UP };
    }
    
    public enum OffsetType {
        NONE, 
        XZ, 
        XYZ;
    }
    
    public static class Properties {
        private Material material;
        private Function<BlockState, MaterialColor> materialColor;
        private boolean hasCollision;
        private SoundType soundType;
        private ToIntFunction<BlockState> lightEmission;
        private float explosionResistance;
        private float destroyTime;
        private boolean requiresCorrectToolForDrops;
        private boolean isRandomlyTicking;
        private float friction;
        private float speedFactor;
        private float jumpFactor;
        private ResourceLocation drops;
        private boolean canOcclude;
        private boolean isAir;
        private StateArgumentPredicate<EntityType<?>> isValidSpawn;
        private StatePredicate isRedstoneConductor;
        private StatePredicate isSuffocating;
        private StatePredicate isViewBlocking;
        private StatePredicate hasPostProcess;
        private StatePredicate emissiveRendering;
        private boolean dynamicShape;
        
        private Properties(final Material cxd, final MaterialColor cxe) {
            this(cxd, cfj -> cxe);
        }
        
        private Properties(final Material cxd, final Function<BlockState, MaterialColor> function) {
            this.hasCollision = true;
            this.soundType = SoundType.STONE;
            this.lightEmission = (cfj -> 0);
            this.friction = 0.6f;
            this.speedFactor = 1.0f;
            this.jumpFactor = 1.0f;
            this.canOcclude = true;
            this.isValidSpawn = ((cfj, bpg, fu, aoq) -> cfj.isFaceSturdy(bpg, fu, Direction.UP) && cfj.getLightEmission() < 14);
            this.isRedstoneConductor = ((cfj, bpg, fu) -> cfj.getMaterial().isSolidBlocking() && cfj.isCollisionShapeFullBlock(bpg, fu));
            this.isSuffocating = ((cfj, bpg, fu) -> this.material.blocksMotion() && cfj.isCollisionShapeFullBlock(bpg, fu));
            this.isViewBlocking = this.isSuffocating;
            this.hasPostProcess = ((cfj, bpg, fu) -> false);
            this.emissiveRendering = ((cfj, bpg, fu) -> false);
            this.material = cxd;
            this.materialColor = function;
        }
        
        public static Properties of(final Material cxd) {
            return of(cxd, cxd.getColor());
        }
        
        public static Properties of(final Material cxd, final DyeColor bje) {
            return of(cxd, bje.getMaterialColor());
        }
        
        public static Properties of(final Material cxd, final MaterialColor cxe) {
            return new Properties(cxd, cxe);
        }
        
        public static Properties of(final Material cxd, final Function<BlockState, MaterialColor> function) {
            return new Properties(cxd, function);
        }
        
        public static Properties copy(final BlockBehaviour cfi) {
            final Properties c2 = new Properties(cfi.material, cfi.properties.materialColor);
            c2.material = cfi.properties.material;
            c2.destroyTime = cfi.properties.destroyTime;
            c2.explosionResistance = cfi.properties.explosionResistance;
            c2.hasCollision = cfi.properties.hasCollision;
            c2.isRandomlyTicking = cfi.properties.isRandomlyTicking;
            c2.lightEmission = cfi.properties.lightEmission;
            c2.materialColor = cfi.properties.materialColor;
            c2.soundType = cfi.properties.soundType;
            c2.friction = cfi.properties.friction;
            c2.speedFactor = cfi.properties.speedFactor;
            c2.dynamicShape = cfi.properties.dynamicShape;
            c2.canOcclude = cfi.properties.canOcclude;
            c2.isAir = cfi.properties.isAir;
            c2.requiresCorrectToolForDrops = cfi.properties.requiresCorrectToolForDrops;
            return c2;
        }
        
        public Properties noCollission() {
            this.hasCollision = false;
            this.canOcclude = false;
            return this;
        }
        
        public Properties noOcclusion() {
            this.canOcclude = false;
            return this;
        }
        
        public Properties friction(final float float1) {
            this.friction = float1;
            return this;
        }
        
        public Properties speedFactor(final float float1) {
            this.speedFactor = float1;
            return this;
        }
        
        public Properties jumpFactor(final float float1) {
            this.jumpFactor = float1;
            return this;
        }
        
        public Properties sound(final SoundType cbh) {
            this.soundType = cbh;
            return this;
        }
        
        public Properties lightLevel(final ToIntFunction<BlockState> toIntFunction) {
            this.lightEmission = toIntFunction;
            return this;
        }
        
        public Properties strength(final float float1, final float float2) {
            this.destroyTime = float1;
            this.explosionResistance = Math.max(0.0f, float2);
            return this;
        }
        
        public Properties instabreak() {
            return this.strength(0.0f);
        }
        
        public Properties strength(final float float1) {
            this.strength(float1, float1);
            return this;
        }
        
        public Properties randomTicks() {
            this.isRandomlyTicking = true;
            return this;
        }
        
        public Properties dynamicShape() {
            this.dynamicShape = true;
            return this;
        }
        
        public Properties noDrops() {
            this.drops = BuiltInLootTables.EMPTY;
            return this;
        }
        
        public Properties dropsLike(final Block bvr) {
            this.drops = bvr.getLootTable();
            return this;
        }
        
        public Properties air() {
            this.isAir = true;
            return this;
        }
        
        public Properties isValidSpawn(final StateArgumentPredicate<EntityType<?>> d) {
            this.isValidSpawn = d;
            return this;
        }
        
        public Properties isRedstoneConductor(final StatePredicate e) {
            this.isRedstoneConductor = e;
            return this;
        }
        
        public Properties isSuffocating(final StatePredicate e) {
            this.isSuffocating = e;
            return this;
        }
        
        public Properties isViewBlocking(final StatePredicate e) {
            this.isViewBlocking = e;
            return this;
        }
        
        public Properties hasPostProcess(final StatePredicate e) {
            this.hasPostProcess = e;
            return this;
        }
        
        public Properties emissiveRendering(final StatePredicate e) {
            this.emissiveRendering = e;
            return this;
        }
        
        public Properties requiresCorrectToolForDrops() {
            this.requiresCorrectToolForDrops = true;
            return this;
        }
    }
    
    public abstract static class BlockStateBase extends StateHolder<Block, BlockState> {
        private final int lightEmission;
        private final boolean useShapeForLightOcclusion;
        private final boolean isAir;
        private final Material material;
        private final MaterialColor materialColor;
        private final float destroySpeed;
        private final boolean requiresCorrectToolForDrops;
        private final boolean canOcclude;
        private final StatePredicate isRedstoneConductor;
        private final StatePredicate isSuffocating;
        private final StatePredicate isViewBlocking;
        private final StatePredicate hasPostProcess;
        private final StatePredicate emissiveRendering;
        @Nullable
        protected Cache cache;
        
        protected BlockStateBase(final Block bvr, final ImmutableMap<Property<?>, Comparable<?>> immutableMap, final MapCodec<BlockState> mapCodec) {
            super(bvr, immutableMap, mapCodec);
            final Properties c5 = bvr.properties;
            this.lightEmission = c5.lightEmission.applyAsInt(this.asState());
            this.useShapeForLightOcclusion = bvr.useShapeForLightOcclusion(this.asState());
            this.isAir = c5.isAir;
            this.material = c5.material;
            this.materialColor = c5.materialColor.apply(this.asState());
            this.destroySpeed = c5.destroyTime;
            this.requiresCorrectToolForDrops = c5.requiresCorrectToolForDrops;
            this.canOcclude = c5.canOcclude;
            this.isRedstoneConductor = c5.isRedstoneConductor;
            this.isSuffocating = c5.isSuffocating;
            this.isViewBlocking = c5.isViewBlocking;
            this.hasPostProcess = c5.hasPostProcess;
            this.emissiveRendering = c5.emissiveRendering;
        }
        
        public void initCache() {
            if (!this.getBlock().hasDynamicShape()) {
                this.cache = new Cache(this.asState());
            }
        }
        
        public Block getBlock() {
            return (Block)this.owner;
        }
        
        public Material getMaterial() {
            return this.material;
        }
        
        public boolean isValidSpawn(final BlockGetter bpg, final BlockPos fu, final EntityType<?> aoq) {
            return this.getBlock().properties.isValidSpawn.test(this.asState(), bpg, fu, aoq);
        }
        
        public boolean propagatesSkylightDown(final BlockGetter bpg, final BlockPos fu) {
            if (this.cache != null) {
                return this.cache.propagatesSkylightDown;
            }
            return this.getBlock().propagatesSkylightDown(this.asState(), bpg, fu);
        }
        
        public int getLightBlock(final BlockGetter bpg, final BlockPos fu) {
            if (this.cache != null) {
                return this.cache.lightBlock;
            }
            return this.getBlock().getLightBlock(this.asState(), bpg, fu);
        }
        
        public VoxelShape getFaceOcclusionShape(final BlockGetter bpg, final BlockPos fu, final Direction fz) {
            if (this.cache != null && this.cache.occlusionShapes != null) {
                return this.cache.occlusionShapes[fz.ordinal()];
            }
            return Shapes.getFaceShape(this.getOcclusionShape(bpg, fu), fz);
        }
        
        public VoxelShape getOcclusionShape(final BlockGetter bpg, final BlockPos fu) {
            return this.getBlock().getOcclusionShape(this.asState(), bpg, fu);
        }
        
        public boolean hasLargeCollisionShape() {
            return this.cache == null || this.cache.largeCollisionShape;
        }
        
        public boolean useShapeForLightOcclusion() {
            return this.useShapeForLightOcclusion;
        }
        
        public int getLightEmission() {
            return this.lightEmission;
        }
        
        public boolean isAir() {
            return this.isAir;
        }
        
        public MaterialColor getMapColor(final BlockGetter bpg, final BlockPos fu) {
            return this.materialColor;
        }
        
        public BlockState rotate(final Rotation cap) {
            return this.getBlock().rotate(this.asState(), cap);
        }
        
        public BlockState mirror(final Mirror bzj) {
            return this.getBlock().mirror(this.asState(), bzj);
        }
        
        public RenderShape getRenderShape() {
            return this.getBlock().getRenderShape(this.asState());
        }
        
        public boolean emissiveRendering(final BlockGetter bpg, final BlockPos fu) {
            return this.emissiveRendering.test(this.asState(), bpg, fu);
        }
        
        public float getShadeBrightness(final BlockGetter bpg, final BlockPos fu) {
            return this.getBlock().getShadeBrightness(this.asState(), bpg, fu);
        }
        
        public boolean isRedstoneConductor(final BlockGetter bpg, final BlockPos fu) {
            return this.isRedstoneConductor.test(this.asState(), bpg, fu);
        }
        
        public boolean isSignalSource() {
            return this.getBlock().isSignalSource(this.asState());
        }
        
        public int getSignal(final BlockGetter bpg, final BlockPos fu, final Direction fz) {
            return this.getBlock().getSignal(this.asState(), bpg, fu, fz);
        }
        
        public boolean hasAnalogOutputSignal() {
            return this.getBlock().hasAnalogOutputSignal(this.asState());
        }
        
        public int getAnalogOutputSignal(final Level bqb, final BlockPos fu) {
            return this.getBlock().getAnalogOutputSignal(this.asState(), bqb, fu);
        }
        
        public float getDestroySpeed(final BlockGetter bpg, final BlockPos fu) {
            return this.destroySpeed;
        }
        
        public float getDestroyProgress(final Player bec, final BlockGetter bpg, final BlockPos fu) {
            return this.getBlock().getDestroyProgress(this.asState(), bec, bpg, fu);
        }
        
        public int getDirectSignal(final BlockGetter bpg, final BlockPos fu, final Direction fz) {
            return this.getBlock().getDirectSignal(this.asState(), bpg, fu, fz);
        }
        
        public PushReaction getPistonPushReaction() {
            return this.getBlock().getPistonPushReaction(this.asState());
        }
        
        public boolean isSolidRender(final BlockGetter bpg, final BlockPos fu) {
            if (this.cache != null) {
                return this.cache.solidRender;
            }
            final BlockState cfj4 = this.asState();
            return cfj4.canOcclude() && Block.isShapeFullBlock(cfj4.getOcclusionShape(bpg, fu));
        }
        
        public boolean canOcclude() {
            return this.canOcclude;
        }
        
        public boolean skipRendering(final BlockState cfj, final Direction fz) {
            return this.getBlock().skipRendering(this.asState(), cfj, fz);
        }
        
        public VoxelShape getShape(final BlockGetter bpg, final BlockPos fu) {
            return this.getShape(bpg, fu, CollisionContext.empty());
        }
        
        public VoxelShape getShape(final BlockGetter bpg, final BlockPos fu, final CollisionContext der) {
            return this.getBlock().getShape(this.asState(), bpg, fu, der);
        }
        
        public VoxelShape getCollisionShape(final BlockGetter bpg, final BlockPos fu) {
            if (this.cache != null) {
                return this.cache.collisionShape;
            }
            return this.getCollisionShape(bpg, fu, CollisionContext.empty());
        }
        
        public VoxelShape getCollisionShape(final BlockGetter bpg, final BlockPos fu, final CollisionContext der) {
            return this.getBlock().getCollisionShape(this.asState(), bpg, fu, der);
        }
        
        public VoxelShape getBlockSupportShape(final BlockGetter bpg, final BlockPos fu) {
            return this.getBlock().getBlockSupportShape(this.asState(), bpg, fu);
        }
        
        public VoxelShape getVisualShape(final BlockGetter bpg, final BlockPos fu, final CollisionContext der) {
            return this.getBlock().getVisualShape(this.asState(), bpg, fu, der);
        }
        
        public VoxelShape getInteractionShape(final BlockGetter bpg, final BlockPos fu) {
            return this.getBlock().getInteractionShape(this.asState(), bpg, fu);
        }
        
        public final boolean entityCanStandOn(final BlockGetter bpg, final BlockPos fu, final Entity aom) {
            return this.entityCanStandOnFace(bpg, fu, aom, Direction.UP);
        }
        
        public final boolean entityCanStandOnFace(final BlockGetter bpg, final BlockPos fu, final Entity aom, final Direction fz) {
            return Block.isFaceFull(this.getCollisionShape(bpg, fu, CollisionContext.of(aom)), fz);
        }
        
        public Vec3 getOffset(final BlockGetter bpg, final BlockPos fu) {
            final OffsetType b4 = this.getBlock().getOffsetType();
            if (b4 == OffsetType.NONE) {
                return Vec3.ZERO;
            }
            final long long5 = Mth.getSeed(fu.getX(), 0, fu.getZ());
            return new Vec3(((long5 & 0xFL) / 15.0f - 0.5) * 0.5, (b4 == OffsetType.XYZ) ? (((long5 >> 4 & 0xFL) / 15.0f - 1.0) * 0.2) : 0.0, ((long5 >> 8 & 0xFL) / 15.0f - 0.5) * 0.5);
        }
        
        public boolean triggerEvent(final Level bqb, final BlockPos fu, final int integer3, final int integer4) {
            return this.getBlock().triggerEvent(this.asState(), bqb, fu, integer3, integer4);
        }
        
        public void neighborChanged(final Level bqb, final BlockPos fu2, final Block bvr, final BlockPos fu4, final boolean boolean5) {
            this.getBlock().neighborChanged(this.asState(), bqb, fu2, bvr, fu4, boolean5);
        }
        
        public final void updateNeighbourShapes(final LevelAccessor bqc, final BlockPos fu, final int integer) {
            this.updateNeighbourShapes(bqc, fu, integer, 512);
        }
        
        public final void updateNeighbourShapes(final LevelAccessor bqc, final BlockPos fu, final int integer3, final int integer4) {
            this.getBlock();
            final BlockPos.MutableBlockPos a6 = new BlockPos.MutableBlockPos();
            for (final Direction fz10 : BlockBehaviour.UPDATE_SHAPE_ORDER) {
                a6.setWithOffset(fu, fz10);
                final BlockState cfj11 = bqc.getBlockState(a6);
                final BlockState cfj12 = cfj11.updateShape(fz10.getOpposite(), this.asState(), bqc, a6, fu);
                Block.updateOrDestroy(cfj11, cfj12, bqc, a6, integer3, integer4);
            }
        }
        
        public final void updateIndirectNeighbourShapes(final LevelAccessor bqc, final BlockPos fu, final int integer) {
            this.updateIndirectNeighbourShapes(bqc, fu, integer, 512);
        }
        
        public void updateIndirectNeighbourShapes(final LevelAccessor bqc, final BlockPos fu, final int integer3, final int integer4) {
            this.getBlock().updateIndirectNeighbourShapes(this.asState(), bqc, fu, integer3, integer4);
        }
        
        public void onPlace(final Level bqb, final BlockPos fu, final BlockState cfj, final boolean boolean4) {
            this.getBlock().onPlace(this.asState(), bqb, fu, cfj, boolean4);
        }
        
        public void onRemove(final Level bqb, final BlockPos fu, final BlockState cfj, final boolean boolean4) {
            this.getBlock().onRemove(this.asState(), bqb, fu, cfj, boolean4);
        }
        
        public void tick(final ServerLevel zd, final BlockPos fu, final Random random) {
            this.getBlock().tick(this.asState(), zd, fu, random);
        }
        
        public void randomTick(final ServerLevel zd, final BlockPos fu, final Random random) {
            this.getBlock().randomTick(this.asState(), zd, fu, random);
        }
        
        public void entityInside(final Level bqb, final BlockPos fu, final Entity aom) {
            this.getBlock().entityInside(this.asState(), bqb, fu, aom);
        }
        
        public void spawnAfterBreak(final Level bqb, final BlockPos fu, final ItemStack bki) {
            this.getBlock().spawnAfterBreak(this.asState(), bqb, fu, bki);
        }
        
        public List<ItemStack> getDrops(final LootContext.Builder a) {
            return this.getBlock().getDrops(this.asState(), a);
        }
        
        public InteractionResult use(final Level bqb, final Player bec, final InteractionHand anf, final BlockHitResult deh) {
            return this.getBlock().use(this.asState(), bqb, deh.getBlockPos(), bec, anf, deh);
        }
        
        public void attack(final Level bqb, final BlockPos fu, final Player bec) {
            this.getBlock().attack(this.asState(), bqb, fu, bec);
        }
        
        public boolean isSuffocating(final BlockGetter bpg, final BlockPos fu) {
            return this.isSuffocating.test(this.asState(), bpg, fu);
        }
        
        public boolean isViewBlocking(final BlockGetter bpg, final BlockPos fu) {
            return this.isViewBlocking.test(this.asState(), bpg, fu);
        }
        
        public BlockState updateShape(final Direction fz, final BlockState cfj, final LevelAccessor bqc, final BlockPos fu4, final BlockPos fu5) {
            return this.getBlock().updateShape(this.asState(), fz, cfj, bqc, fu4, fu5);
        }
        
        public boolean isPathfindable(final BlockGetter bpg, final BlockPos fu, final PathComputationType czg) {
            return this.getBlock().isPathfindable(this.asState(), bpg, fu, czg);
        }
        
        public boolean canBeReplaced(final BlockPlaceContext bin) {
            return this.getBlock().canBeReplaced(this.asState(), bin);
        }
        
        public boolean canBeReplaced(final Fluid cwz) {
            return this.getBlock().canBeReplaced(this.asState(), cwz);
        }
        
        public boolean canSurvive(final LevelReader bqd, final BlockPos fu) {
            return this.getBlock().canSurvive(this.asState(), bqd, fu);
        }
        
        public boolean hasPostProcess(final BlockGetter bpg, final BlockPos fu) {
            return this.hasPostProcess.test(this.asState(), bpg, fu);
        }
        
        @Nullable
        public MenuProvider getMenuProvider(final Level bqb, final BlockPos fu) {
            return this.getBlock().getMenuProvider(this.asState(), bqb, fu);
        }
        
        public boolean is(final Tag<Block> adf) {
            return this.getBlock().is(adf);
        }
        
        public boolean is(final Tag<Block> adf, final Predicate<BlockStateBase> predicate) {
            return this.getBlock().is(adf) && predicate.test(this);
        }
        
        public boolean is(final Block bvr) {
            return this.getBlock().is(bvr);
        }
        
        public FluidState getFluidState() {
            return this.getBlock().getFluidState(this.asState());
        }
        
        public boolean isRandomlyTicking() {
            return this.getBlock().isRandomlyTicking(this.asState());
        }
        
        public long getSeed(final BlockPos fu) {
            return this.getBlock().getSeed(this.asState(), fu);
        }
        
        public SoundType getSoundType() {
            return this.getBlock().getSoundType(this.asState());
        }
        
        public void onProjectileHit(final Level bqb, final BlockState cfj, final BlockHitResult deh, final Projectile bes) {
            this.getBlock().onProjectileHit(bqb, cfj, deh, bes);
        }
        
        public boolean isFaceSturdy(final BlockGetter bpg, final BlockPos fu, final Direction fz) {
            if (this.cache != null) {
                return this.cache.isFaceSturdy[fz.ordinal()];
            }
            return Block.isFaceSturdy(this.asState(), bpg, fu, fz);
        }
        
        public boolean isCollisionShapeFullBlock(final BlockGetter bpg, final BlockPos fu) {
            if (this.cache != null) {
                return this.cache.isCollisionShapeFullBlock;
            }
            return Block.isShapeFullBlock(this.getCollisionShape(bpg, fu));
        }
        
        protected abstract BlockState asState();
        
        public boolean requiresCorrectToolForDrops() {
            return this.requiresCorrectToolForDrops;
        }
        
        static final class Cache {
            private static final Direction[] DIRECTIONS;
            protected final boolean solidRender;
            private final boolean propagatesSkylightDown;
            private final int lightBlock;
            @Nullable
            private final VoxelShape[] occlusionShapes;
            protected final VoxelShape collisionShape;
            protected final boolean largeCollisionShape;
            protected final boolean[] isFaceSturdy;
            protected final boolean isCollisionShapeFullBlock;
            
            private Cache(final BlockState cfj) {
                final Block bvr3 = cfj.getBlock();
                this.solidRender = cfj.isSolidRender(EmptyBlockGetter.INSTANCE, BlockPos.ZERO);
                this.propagatesSkylightDown = bvr3.propagatesSkylightDown(cfj, EmptyBlockGetter.INSTANCE, BlockPos.ZERO);
                this.lightBlock = bvr3.getLightBlock(cfj, EmptyBlockGetter.INSTANCE, BlockPos.ZERO);
                if (!cfj.canOcclude()) {
                    this.occlusionShapes = null;
                }
                else {
                    this.occlusionShapes = new VoxelShape[Cache.DIRECTIONS.length];
                    final VoxelShape dfg4 = bvr3.getOcclusionShape(cfj, EmptyBlockGetter.INSTANCE, BlockPos.ZERO);
                    for (final Direction fz8 : Cache.DIRECTIONS) {
                        this.occlusionShapes[fz8.ordinal()] = Shapes.getFaceShape(dfg4, fz8);
                    }
                }
                this.collisionShape = bvr3.getCollisionShape(cfj, EmptyBlockGetter.INSTANCE, BlockPos.ZERO, CollisionContext.empty());
                this.largeCollisionShape = Arrays.<Direction.Axis>stream(Direction.Axis.values()).anyMatch(a -> this.collisionShape.min(a) < 0.0 || this.collisionShape.max(a) > 1.0);
                this.isFaceSturdy = new boolean[6];
                for (final Direction fz9 : Cache.DIRECTIONS) {
                    this.isFaceSturdy[fz9.ordinal()] = Block.isFaceSturdy(cfj, EmptyBlockGetter.INSTANCE, BlockPos.ZERO, fz9);
                }
                this.isCollisionShapeFullBlock = Block.isShapeFullBlock(cfj.getCollisionShape(EmptyBlockGetter.INSTANCE, BlockPos.ZERO));
            }
            
            static {
                DIRECTIONS = Direction.values();
            }
        }
    }
    
    public interface StateArgumentPredicate<A> {
        boolean test(final BlockState cfj, final BlockGetter bpg, final BlockPos fu, final A object);
    }
    
    public interface StatePredicate {
        boolean test(final BlockState cfj, final BlockGetter bpg, final BlockPos fu);
    }
}
