SforzandoCF

thing

Oct 8th, 2024 (edited)
114
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
Java 82.05 KB | None | 0 0
  1. public final class DetectorBlock extends RedstoneBlock implements BlockEntityProvider {
  2.     public static final Property FACING = Properties.FACING;
  3.    
  4.     public DetectorBlock (AbstractBlock.Settings settings) {
  5.         super (settings);
  6.         this.setDefaultState(this.getDefaultState().with(FACING, Direction.SOUTH));
  7.     }
  8.    
  9.     public BlockState getPlacementState (ItemPlacementContext ctx) {
  10.         return this.getDefaultState().with(FACING, ctx.getHorizontalPlayerFacing().getOpposite());
  11.     }
  12.    
  13.     protected int getWeakRedstonePower (BlockState state, BlockView world, BlockPos pos, Direction dir) {
  14.         if (!state.hasProperty(FACING) || dir != state.getValue(FACING).getOpposite()) return 0;
  15.         return (this.getOrCreateBlockEntity(world, pos).isDetecting()) ? 15 : 0;
  16.     }
  17.    
  18.     public DetectorBlockEntity createBlockEntity (BlockPos pos, BlockState state) {
  19.         return new DetectorBlockEntity (pos, state);
  20.     }
  21.    
  22.     public BlockEntityTicker<DetectorBlockEntity> getTicker (World world, BlockState state, BlockEntityType<DetectorBlockEntity> type) {
  23.         return (w, p, s, e) -> { e.tick(); };
  24.     }
  25. }
  26.  
  27. public final class DetectorBlockEntity extends BlockEntity {
  28.     private final BlockPos detectionPosition;
  29.     private boolean isDetecting = false;
  30.    
  31.     private int entityCount = 0;
  32.     private BlockState targetState = null;
  33.    
  34.     private int entityCountO = 0;
  35.     private BlockState targetStateO = null;
  36.    
  37.     public DetectorBlockEntity (BlockPos pos, BlockState state) {
  38.         super(BTWBlockEntities.DETECTOR_BLOCK, pos, state);
  39.         this.detectionPosition = pos.offset(1L, state.hasProperty(DetectorBlock.FACING) ? state.getValue(DetectorBlock.FACING) : Direction.NORTH);
  40.     }
  41.    
  42.     public void setWorld (World world) {
  43.         super.setWorld(world);
  44.         this.targetStateO = world.getBlockState(this.detectionPosition);
  45.         this.targetState = world.getBlockState(this.detectionPosition);
  46.     }
  47.    
  48.     public void tick () {
  49.         if (!this.hasWorld()) return;
  50.         this.entityCount = this.getWorld().getOtherEntities(null, new Box(this.detectionPosition)).size();
  51.         this.targetState = this.getWorld().getBlockState(this.detectionPosition);
  52.         this.isDetecting = this.entityCount != this.entityCountO || this.targetState != this.targetStateO || this.checkWeather();
  53.         this.entityCountO = this.entityCount;
  54.         this.targetStateO = this.targetState;
  55.     }
  56.    
  57.     private boolean checkWeather () {
  58.         return this.detectionPosition.down() == this.getPos() && this.getWorld().isSkyVisible(this.detectionPosition) && this.getWorld().isRainingAt(this.detectionPosition) && this.getWorld().isThundering();
  59.     }
  60.    
  61.     public boolean isDetecting () {
  62.         return this.isDetecting;
  63.     }
  64. }
  65.  
  66. public class BuddyBlock extends RedstoneBlock {
  67.     public BuddyBlock (AbstractBlock.Settings settings) {
  68.         super (settings);
  69.     }
  70.    
  71.     public void neighborUpdate (BlockState state, World world, BlockPos pos, Block sourceBlock, BlockPos sourcePos, boolean notify) {
  72.         if (state.hasProperty(ACTIVE) && state.getValue(ACTIVE).booleanValue()) {
  73.             state.with(ACTIVE, Boolean.FALSE);
  74.             return;
  75.         }
  76.         if (pos.down() == sourcePos) return;
  77.         state.withIfExists(ACTIVE, Boolean.TRUE);
  78.     }
  79.    
  80.     protected int getWeakRedstonePower (BlockState state, BlockView world, BlockPos pos, Direction dir) {
  81.         return (state.hasProperty(ACTIVE) && state.getValue(ACTIVE).booleanValue()) ? 15 : 0;
  82.     }
  83. }
  84.  
  85. public final class HibachiBlock extends BlockWithEntity {
  86.     public HibachiBlock (AbstractBlock.Settings settings) {
  87.         super (settings);
  88.     }
  89.    
  90.     public MapCodec<HibachiBlock> getCodec () {
  91.         return AbstractBlock.simpleCodec(HibachiBlock::new);
  92.     }
  93.    
  94.     public void neighborUpdate (BlockState state, World world, BlockPos pos, Block sourceBlock, BlockPos sourcePos, boolean notify) {
  95.         if (!state.hasProperty(ACTIVE)) return;
  96.         if (pos.up() == sourcePos && !state.getValue(ACTIVE).booleanValue() && sourceBlock instanceof AbstractFireBlock)
  97.             this.getOrCreateBlockEntity(world, pos).setExtinguishing();
  98.         if (pos.up() != sourcePos && sourceBlock instanceof RedstoneBlock rb)
  99.             if (rb.getWeakRedstonePower(world.getBlockState(sourcePos), world, sourcePos, this.directionTo(sourcePos, pos)) != 0)
  100.                 this.getOrCreateBlockEntity(world, pos).setIgniting();
  101.     }
  102.    
  103.     private Direction directionTo (BlockPos source, BlockPos to) {
  104.         for (Direction d : Direction.stream().collect(Collectors.asList()))
  105.             if (to.offset(1L, d) == source)
  106.                 return d;
  107.         throw new IllegalArgumentException("Source position at x:" + source.getX() + ", y:" + source.getY() + ", z:" + source.getZ() + " is not adjacent to target position at x:" + source.getX() + ", y:" + source.getY() + ", z:" + source.getZ());
  108.     }
  109.    
  110.     private HibachiBlockEntity getOrCreateBlockEntity (World world, BlockPos pos) {
  111.         if (world.getBlockEntity(pos) instanceof HibachiBlockEntity) {
  112.             return world.getBlockEntity(pos);
  113.         } else {
  114.             HibachiBlockEntity e = this.newBlockEntity(world.getBlockState(pos), pos);
  115.             e.setWorld(world);
  116.             return e;
  117.         }
  118.     }
  119.    
  120.     public HibachiBlockEntity createBlockEntity (BlockState state, BlockPos pos) {
  121.         return new HibachiBlockEntity(state, pos);
  122.     }
  123. }
  124.  
  125. public final class HibachiBlockEntity extends BlockEntity {
  126.     private int extinguishTimer = 0;
  127.     private int igniteTimer = 0;
  128.     private int stokeTimer = 0;
  129.    
  130.     private boolean stokeNearby = false;
  131.    
  132.     public HibachiBlockEntity (BlockState state, BlockPos pos) {
  133.         super (BTWBlockEntities.HIBACHI, state, pos);
  134.     }
  135.    
  136.     void setExtinguishing () {
  137.         this.extinguishTimer = 20;
  138.     }
  139.    
  140.     void setIgniting () {
  141.         this.igniteTimer = 10;
  142.     }
  143.    
  144.     void setStoking (boolean stokeOthers) {
  145.         this.stokeTimer = 30;
  146.         this.stokeNearby = stokeOthers;
  147.     }
  148.    
  149.     public void tick () {
  150.         this.extinguishTimer--;
  151.         if (this.extinguishTimer == 0) this.extinguish();
  152.         this.igniteTimer--;
  153.         if (this.igniteTimer == 0) this.ignite();
  154.         this.stokeTimer--;
  155.         if (this.stokeTimer == 0) this.stoke();
  156.         if (this.stokeTimer == -40) this.destoke();
  157.     }
  158.    
  159.     private void extinguish () {
  160.         if (this.getWorld().getBlockState(this.getPos().up()).getBlock() instanceof AbstractFireBlock) {
  161.             this.getWorld().setBlockState(this.getPos().up(), Blocks.AIR.getDefaultBlockState());
  162.             this.getWorld().playSound((Entity) null, this.getPos().up(), SoundEvents.BLOCK_FIRE_EXTINGUISH, SoundCategory.BLOCKS, 0.75F, 1F);
  163.         }
  164.     }
  165.    
  166.     private void ignite () {
  167.         this.getWorld().setBlockState(this.getPos().up(), Blocks.AIR.getDefaultBlockState());
  168.         BlockPos pos = this.getNearestBellows();
  169.         if (pos != this.getPos() && this.getWorld().getBlockState(pos).isActive(this.getWorld(), pos)) this.setStoking(true);
  170.     }
  171.    
  172.     private void stoke () {
  173.         this.getWorld().setBlockState(this.getPos().up(), BTWBlocks.STOKED_FIRE.getDefaultBlockState());
  174.         if (this.stokeNearby) {
  175.             if (this.getWorld().getBlockState(this.getPos().south()).getBlock() instanceof HibachiBlock) ((HibachiBlockEntity) this.getWorld().getBlockEntity(this.getPos().south())).setStoking(false);
  176.             if (this.getWorld().getBlockState(this.getPos().north()).getBlock() instanceof HibachiBlock) ((HibachiBlockEntity) this.getWorld().getBlockEntity(this.getPos().north())).setStoking(false);
  177.             if (this.getWorld().getBlockState(this.getPos().east()).getBlock() instanceof HibachiBlock) ((HibachiBlockEntity) this.getWorld().getBlockEntity(this.getPos().east())).setStoking(false);
  178.             if (this.getWorld().getBlockState(this.getPos().west()).getBlock() instanceof HibachiBlock) ((HibachiBlockEntity) this.getWorld().getBlockEntity(this.getPos().west())).setStoking(false);
  179.         }
  180.     }
  181.    
  182.     private void destoke () {
  183.         this.getWorld().setBlockState(this.getPos().up(), Blocks.FIRE.getDefaultBlockState());
  184.     }
  185.    
  186.     private BlockPos getNearestBellows () {
  187.         for (Direction d : Direction.stream().collect(Collectors.asList())) {
  188.             if (d.getAxis() == Direction.Axis.Y) continue;
  189.             BlockPos[] firstRange = new BlockPos[3] { this.getPos().up().offset(1L, d).offset(1L, d.rotateYClockwise()), this.getPos().up().offset(1L, d), this.getPos().up().offset(1L, d).offset(1L, d.rotateYCounterclockwise()) };
  190.             for (BlockPos p : firstRange)
  191.                 if (this.getWorld().getBlockState(p).getBlock() instanceof BellowsBlock && this.getWorld().getBlockState(p).hasProperty(HorizontalFacingBlock.FACING) && (this.getWorld().getBlockState(p).getValue(HorizontalFacingBlock.FACING).getOpposite() == d))
  192.                     return p;
  193.             BlockPos[] secondRange = new BlockPos[3] { this.getPos().up().offset(2L, d).offset(1L, d.rotateYClockwise()), this.getPos().up().offset(2L, d), this.getPos().up().offset(2L, d).offset(1L, d.rotateYCounterclockwise()) };
  194.             for (BlockPos p : secondRange)
  195.                 if (this.getWorld().getBlockState(p).getBlock() instanceof BellowsBlock && this.getWorld().getBlockState(p).hasProperty(HorizontalFacingBlock.FACING) && (this.getWorld().getBlockState(p).getValue(HorizontalFacingBlock.FACING).getOpposite() == d))
  196.                     return p;
  197.             BlockPos[] thirdRange = new BlockPos[5] { this.getPos().up().offset(3L, d).offset(2L, d.rotateYClockwise()), this.getPos().up().offset(3L, d).offset(1L, d.rotateYClockwise()), this.getPos().up().offset(3L, d), this.getPos().up().offset(3L, d).offset(1L, d.rotateYCounterclockwise()), this.getPos().up().offset(3L, d).offset(2L, d.rotateYCounterclockwise()) };
  198.             for (BlockPos p : thirdRange)
  199.                 if (this.getWorld().getBlockState(p).getBlock() instanceof BellowsBlock && this.getWorld().getBlockState(p).hasProperty(HorizontalFacingBlock.FACING) && (this.getWorld().getBlockState(p).getValue(HorizontalFacingBlock.FACING).getOpposite() == d))
  200.                     return p;
  201.         }
  202.         return this.getPos();
  203.     }
  204. }
  205.  
  206. public interface MechanicalPowerBlock {
  207.     boolean recievesPower (World world, BlockPos pos, Direction toDir);
  208.    
  209.     boolean isSimple ();
  210.    
  211.     void rotationTick (BlockState state, World world, BlockPos pos);
  212.    
  213.     default void baseRotationTick (BlockState state, World world, BlockPos pos) {
  214.         this.rotationTick(state, world, pos);
  215.         state.withIfExists(Properties.ACTIVE, Boolean.TRUE);
  216.     }
  217.    
  218.     default void rotationStopped (BlockState state, World world, BlockPos pos) {
  219.         for (Direction d : Direction.stream().collect(Collectors.asList())) {
  220.             if (!this.recievesPower(world, pos, d)) continue;
  221.             if (!(state.getBlock() instanceof MechanicalPowerBlock)) continue;
  222.             if (!state.hasProperty(Properties.ACTIVE)) continue;
  223.             if (!state.getValue(Properties.ACTIVE).booleanValue()) continue;
  224.             return;
  225.         }
  226.         state.withIfExists(Properties.ACTIVE, Boolean.FALSE);
  227.     }
  228.    
  229.     default void isActive (BlockState state) {
  230.         return state.hasProperty(Properties.ACTIVE) && state.getValue(Properties.ACTIVE).booleanValue();
  231.     }
  232.    
  233.     default boolean suppliesPower () {
  234.         return this instanceof MechanicalPowerSupplier;
  235.     }
  236. }
  237.  
  238. public interface MechanicalPowerSupplier {
  239.     void dispatchRotationTick ();
  240.    
  241.     void dispatchRotationStopped ();
  242. }
  243.  
  244. public class AxleBlock extends Block implements MechanicalPowerBlock, Waterloggable {
  245.     public static final DirectionProperty DIRECTION = Properties.FACING;
  246.     public static final BooleanProperty WATERLOGGED = Properties.WATERLOGGED;
  247.    
  248.     public AxleBlock (AbstractBlock.Settings settings) {
  249.         super(settings);
  250.         this.setDefaultBlockState(this.getDefaultBlockState().with(DIRECTION, Direction.UP).with(WATERLOGGED, Boolean.FALSE));
  251.     }
  252.    
  253.     public BlockState getPlacementState (ItemPlacementContext ctx) {
  254.         return this.getDefaultBlockState().with(DIRECTION, ctx.getSide()).with(WATERLOGGED, Boolean.valueOf(ctx.getWorld().getFluidState(ctx.getBlockPos()).isEqualAndStill(Fluids.WATER)));
  255.     }
  256.    
  257.     protected VoxelShape getRaycastShape (BlockState state, BlockView world, BlockPos pos) {
  258.         return this.getShape(state.hasProperty(DIRECTION) ? state.getValue(DIRECTION).getAxis() : Direction.Axis.Y);
  259.     }
  260.    
  261.     protected VoxelShape getOutlineShape (BlockState state, BlockView world, BlockPos pos, ShapeContext context) {
  262.         return this.getShape(state.hasProperty(DIRECTION) ? state.getValue(DIRECTION).getAxis() : Direction.Axis.Y);
  263.     }
  264.    
  265.     protected VoxelShape getCollisionShape (BlockState state, BlockView world, BlockPos pos, ShapeContext context) {
  266.         return this.getShape(state.hasProperty(DIRECTION) ? state.getValue(DIRECTION).getAxis() : Direction.Axis.Y);
  267.     }
  268.    
  269.     private VoxelShape getShape (Direction.Axis axis) {
  270.         if (axis == Direction.Axis.X)
  271.             return VoxelShapes.cuboid(0.0, 5.0, 5.0, 16.0, 11.0, 11.0);
  272.         if (axis == Direction.Axis.Y)
  273.             return VoxelShapes.cuboid(5.0, 0.0, 5.0, 11.0, 16.0, 11.0);
  274.         if (axis == Direction.Axis.Z)
  275.             return VoxelShapes.cuboid(5.0, 5.0, 0.0, 11.0, 11.0, 16.0);
  276.         return VoxelShapes.empty();
  277.     }
  278.    
  279.     public boolean recievesPower (World world, BlockPos pos, Direction toDir) {
  280.         return world.getBlockState(pos).hasProperty(DIRECTION) && (world.getBlockState(pos).getValue(DIRECTION) == toDir.getOpposite());
  281.     }
  282.    
  283.     public boolean isSimple () {
  284.         return false;
  285.     }
  286.    
  287.     public void rotationTick (BlockState state, World world, BlockPos pos) {
  288.         if (!state.hasProperty(DIRECTION)) return;
  289.         BlockPos targetPos = pos.offset(1L, state.getValue(DIRECTION));
  290.         BlockState targetState = world.getBlockState(targetPos);
  291.         if (targetState.getBlock() instanceof MechanicalPowerBlock mpb && mpb.recievesPower(world, pos.offset(1L, state.getValue(DIRECTION)), state.getValue(DIRECTION).getOpposite())) mpb.baseRotationTick(targetState, world, targetPos);
  292.         if (state.hasProperty(GEAR_STATE) && state.getValue(GEAR_STATE).intValue() != 0) this.rotateGears(state, world, pos);
  293.     }
  294.    
  295.     private void rotateGears (BlockState state, World world, BlockPos pos) {
  296.         if (!state.hasProperty(GEAR_STATE)) return;
  297.         state.with(GEAR_STATE, Integer.valueOf(state.getValue(GEAR_STATE).intValue() + 1));
  298.         for (Direction d : Direction.stream().collect(Collectors.asList())) {
  299.             if (d.getAxis() == state.getValue(DIRECTION).getAxis()) continue;
  300.             BlockPos offset = pos.offset(d);
  301.             if (world.getBlockState(offset).hasProperty(DIRECTION) && world.getBlockState(offset).getValue(DIRECTION).getAxis() == state.getValue(DIRECTION).getAxis() && world.getBlockState(offset).hasProperty(GEAR_STATE) && world.getBlockState(offset).getValue(GEAR_STATE).intValue() > 0) {
  302.                 world.setBlockState(offset, world.getBlockState(offset).with(GEAR_STATE, Integer.valueOf(state.getValue(GEAR_STATE).intValue() + 40)));
  303.                 if (world.getBlockState(offset).getBlock() instanceof AxleBlock ab)
  304.                     ab.rotationTick(world.getBlockState(offset), world, offset);
  305.             }
  306.         }
  307.     }
  308.    
  309.     protected void onStateReplaced (BlockState state, World world, BlockPos pos, BlockState newState, boolean moved) {
  310.         List<AbstractMechanicalPowerSupplierEntity> suppliers = world.getEntitiesByClass(AbstractMechanicalPowerSupplierEntity.class, new Box(pos));
  311.         if (suppliers.size() > 0)
  312.             for (AbstractMechanicalPowerSupplierEntity s : suppliers) s.onHostAxleRemoved();
  313.     }
  314. }
  315.  
  316. public class GearboxBlock extends Block implements MechanicalPowerBlock {
  317.     public static final DirectionProperty DIRECTION = Properties.FACING;
  318.    
  319.     public GearboxBlock (AbstractBlock.Settings settings) {
  320.         super(settings);
  321.         this.setDefaultBlockState(this.getDefaultBlockState().with(DIRECTION, Direction.UP));
  322.     }
  323.    
  324.     public BlockState getPlacementState (ItemPlacementContext ctx) {
  325.         return this.getDefaultBlockState().with(DIRECTION, ctx.getSide());
  326.     }
  327.    
  328.     public boolean recievesPower (World world, BlockPos pos, Direction toDir) {
  329.         return world.getBlockState(pos).hasProperty(DIRECTION) && (world.getBlockState(pos).getValue(DIRECTION) == toDir);
  330.     }
  331.    
  332.     public boolean isSimple () {
  333.         return false;
  334.     }
  335.    
  336.     public void rotationTick (BlockState state, World world, BlockPos pos) {
  337.         if (!state.hasProperty(DIRECTION)) return;
  338.         for (Direction d : Direction.stream.collect(Collectors.asList())) {
  339.             if (d == state.getValue(DIRECTION)) continue;
  340.             BlockPos targetPos = pos.offset(1L, d);
  341.             BlockState targetState = world.getBlockState(targetPos);
  342.             if (targetState.getBlock() instanceof AxleBlock ab && ab.recievesPower(world, targetPos, d.getOpposite())) ab.baseRotationTick(targetState, world, targetPos);
  343.         }
  344.         if (world.isClientSide() && world.random.nextInt(20) == 0)
  345.             world.addParticle(ParticleTypes.SMOKE, pos.getX() + random.nextInt(25) / 25, pos.getY() + random.nextInt(25) / 25, pos.getZ() + random.nextInt(25) / 25, 0.0, (random.nextInt(15) + 10) / 25, 0.0)
  346.     }
  347. }
  348.  
  349. public class ClutchBlock extends Block implements MechanicalPowerBlock {
  350.     public static final DirectionProperty DIRECTION = Properties.FACING;
  351.    
  352.     public GearboxBlock (AbstractBlock.Settings settings) {
  353.         super(settings);
  354.         this.setDefaultBlockState(this.getDefaultBlockState().with(DIRECTION, Direction.UP));
  355.     }
  356.    
  357.     public BlockState getPlacementState (ItemPlacementContext ctx) {
  358.         return this.getDefaultBlockState().with(DIRECTION, ctx.getSide());
  359.     }
  360.    
  361.     public boolean recievesPower (World world, BlockPos pos, Direction toDir) {
  362.         return world.getBlockState(pos).hasProperty(DIRECTION) && (world.getBlockState(pos).getValue(DIRECTION) == toDir);
  363.     }
  364.    
  365.     public boolean isSimple () {
  366.         return false;
  367.     }
  368.    
  369.     public void rotationTick (BlockState state, World world, BlockPos pos) {
  370.         if (!state.hasProperty(DIRECTION)) return;
  371.         if (world.getBlockState(pos.up()).getWeakRedstonePower(world, pos.up(), Direction.DOWN) > 0) return;
  372.         for (Direction d : Direction.stream.collect(Collectors.asList())) {
  373.             if (d == state.getValue(DIRECTION) || d = Direction.UP) continue;
  374.             BlockPos targetPos = pos.offset(1L, d);
  375.             BlockState targetState = world.getBlockState(targetPos);
  376.             if (targetState.getBlock() instanceof AxleBlock ab && ab.recievesPower(world, targetPos, d.getOpposite())) ab.baseRotationTick(targetState, world, targetPos);
  377.         }
  378.         if (world.isClientSide() && world.random.nextInt(20) == 0)
  379.             world.addParticle(ParticleTypes.SMOKE, pos.getX() + random.nextInt(25) / 25, pos.getY() + random.nextInt(25) / 25, pos.getZ() + random.nextInt(25) / 25, 0.0, (random.nextInt(15) + 10) / 25, 0.0)
  380.     }
  381. }
  382.  
  383. public abstract class AbstractMechanicalPowerSupplierEntity extends Entity implements MechanicalPowerSupplier {
  384.     protected DataTracker tracker = null;
  385.    
  386.     public static final TrackedData<Boolean> ACTIVE = new TrackedData<Boolean>(0, TrackedDataHandlerRegistry.BOOLEAN);
  387.    
  388.     public AbstractMechanicalPowerSupplierEntity (EntityType<? extends AbstractMechanicalPowerSupplierEntity> type, World world) {
  389.         super (type, world);
  390.     }
  391.    
  392.     public void initDataTracker (DataTracker.Builder builder) {
  393.         this.tracker = builder.add(ACTIVE, Boolean.FALSE).build();
  394.     }
  395.    
  396.     protected void readCustomDataFromNbt (NbtCompound tag) {
  397.         if (this.tracker == null) this.initDataTracker(new DataTracker.Builder(this));
  398.         this.tracker.set(ACTIVE, Boolean.valueOf(tag.getBoolean("active")));
  399.     }
  400.    
  401.     protected void writeCustomDataToNbt (NbtCompound tag) {
  402.         boolean active = this.tracker != null && this.tracker.get(ACTIVE).booleanValue();
  403.         tag.putBoolean("active", active);
  404.     }
  405.    
  406.     public boolean isActive () {
  407.         return this.tracker != null && this.tracker.get(ACTIVE).booleanValue();
  408.     }
  409.    
  410.     public abstract void onHostAxleRemoved ();
  411.    
  412.     public abstract void place (ItemPlacementContext ctx);
  413. }
  414.  
  415. public class WindmillEntity extends AbstractMechanicalPowerSupplierEntity {
  416.     public static final TrackedData<Direction> FACING = new TrackedData<Direction>(0, TrackedDataHandlerRegistry.FACING);
  417.     public static final TrackedData<Boolean> ACTIVEO = new TrackedData<Boolean>(0, TrackedDataHandlerRegistry.BOOLEAN);
  418.    
  419.     public WindmillEntity (World world) {
  420.         super (BTWEntityTypes.WINDMILL, world);
  421.     }
  422.    
  423.     public void initDataTracker (DataTracker.Builder builder) {
  424.         this.tracker = builder.add(ACTIVE, Boolean.FALSE).add(ACTIVEO, Boolean.FALSE).add(FACING, Direction.SOUTH).build();
  425.     }
  426.    
  427.     protected void readCustomDataFromNbt (NbtCompound tag) {
  428.         super.readCustomDataFromNbt(tag);
  429.         this.tracker.set(FACING, Direction.byName(tag.getString("facing")));
  430.         this.tracker.set(ACTIVEO, Boolean.valueOf(tag.getBoolean("activeO")));
  431.     }
  432.    
  433.     protected void writeCustomDataToNbt (NbtCompound tag) {
  434.         super.writeCustomDataToNbt(tag);
  435.         tag.putString("facing", this.tracker != null ? this.tracker.get(FACING).asString() : "south");
  436.         tag.putBoolean("activeO", this.tracker != null && this.tracker.get(ACTIVEO).booleanValue());
  437.     }
  438.    
  439.     public void onHostAxleRemoved () {
  440.         this.dispatchRotationStopped();
  441.         this.remove(Entity.RemovalReason.DISCARDED);
  442.     }
  443.    
  444.     public void place (ItemPlacementContext ctx) {
  445.         BlockState targetState = this.getWorld().getBlockState(ctx.getBlockPos());
  446.         if (targetState.getBlock() instanceof AxleBlock ab) {
  447.             if (targetState.hasProperty(AxleBlock.DIRECTION)) this.tracker.set(FACING, targetState.getValue(AxleBlock.DIRECTION));
  448.             this.setPos(ctx.getBlockPos().getX() + 0.5, ctx.getBlockPos().getY() + 0.5, ctx.getBlockPos().getZ() + 0.5);
  449.         }
  450.     }
  451.    
  452.     public void tick () {
  453.         this.updateActivity();
  454.         if (this.isActive())
  455.             this.dispatchRotationTick();
  456.         else
  457.             if (this.wasActive())
  458.                 this.dispatchRotationStopped();
  459.     }
  460.    
  461.     public void dispatchRotationTick () {
  462.         BlockState state = this.getWorld().getBlockState(this.getBlockPos());
  463.         if (state.getBlock() instanceof AxleBlock ab)
  464.             ab.baseRotationTick(state, this.getWorld(), this.getBlockPos());
  465.     }
  466.    
  467.     public void dispatchRotationStopped () {
  468.         BlockState state = this.getWorld().getBlockState(this.getBlockPos());
  469.         if (state.getBlock() instanceof AxleBlock ab)
  470.             ab.rotationStopped(state, this.getWorld(), this.getBlockPos());
  471.     }
  472.    
  473.     private void wasActive () {
  474.         return this.tracker.get(ACTIVEO).booleanValue();
  475.     }
  476.    
  477.     private void updateActivity () {
  478.         this.tracker.set(ACTIVEO, this.tracker.get(ACTIVE));
  479.         if (this.getWorld().getRegistryKey().getValue().toString() != "minecraft:overworld") {
  480.             this.tracker.set(ACTIVE, Boolean.FALSE);
  481.         } else if (!world.isSkyVisible(this.getBlockPos())) {
  482.             this.tracker.set(ACTIVE, Boolean.FALSE);
  483.         } else {
  484.             this.tracker.set(ACTIVE, Boolean.TRUE);
  485.         }
  486.     }
  487. }
  488.  
  489. public class VerticalWindmillEntity extends AbstractMechanicalPowerSupplierEntity {
  490.     public static final TrackedData<Boolean> FACING = new TrackedData<Boolean>(0, TrackedDataHandlerRegistry.BOOLEAN);
  491.    
  492.     public VerticalWindmillEntity (World world) {
  493.         super (BTWEntityTypes.VERTICAL_WINDMILL, world);
  494.     }
  495.    
  496.     public void initDataTracker (DataTracker.Builder builder) {
  497.         this.tracker = builder.add(ACTIVE, Boolean.FALSE).add(ACTIVEO, Boolean.FALSE).build();
  498.     }
  499.    
  500.     protected void readCustomDataFromNbt (NbtCompound tag) {
  501.         super.readCustomDataFromNbt(tag);
  502.         this.tracker.set(ACTIVEO, Boolean.valueOf(tag.getBoolean("activeO")));
  503.     }
  504.    
  505.     protected void writeCustomDataToNbt (NbtCompound tag) {
  506.         super.writeCustomDataToNbt(tag);
  507.         tag.putBoolean("activeO", this.tracker != null && this.tracker.get(ACTIVEO).booleanValue());
  508.     }
  509.    
  510.     public void onHostAxleRemoved () {
  511.         this.dispatchRotationStopped();
  512.         this.remove(Entity.RemovalReason.DISCARDED);
  513.     }
  514.    
  515.     public void place (ItemPlacementContext ctx) {
  516.         BlockState targetState = this.getWorld().getBlockState(ctx.getBlockPos());
  517.         if (targetState.getBlock() instanceof AxleBlock ab) {
  518.             if (targetState.hasProperty(AxleBlock.DIRECTION)) this.tracker.set(FACING, targetState.getValue(AxleBlock.DIRECTION));
  519.             this.setPos(ctx.getBlockPos().getX() + 0.5, ctx.getBlockPos().getY(), ctx.getBlockPos().getZ() + 0.5);
  520.         }
  521.     }
  522.    
  523.     public void tick () {
  524.         this.updateActivity();
  525.         if (this.isActive())
  526.             this.dispatchRotationTick();
  527.         else
  528.             if (this.wasActive())
  529.                 this.dispatchRotationStopped();
  530.     }
  531.    
  532.     public void dispatchRotationTick () {
  533.         for (int i = 0; i <= 6; i++) {
  534.             BlockState state = this.getWorld().getBlockState(this.getBlockPos().up(i));
  535.             if (state.getBlock() instanceof AxleBlock ab)
  536.                 ab.baseRotationTick(state, this.getWorld(), this.getBlockPos().up(i));
  537.         }
  538.     }
  539.    
  540.     public void dispatchRotationStopped () {
  541.         for (int i = 0; i <= 6; i++) {
  542.             BlockState state = this.getWorld().getBlockState(this.getBlockPos().up(i));
  543.             if (state.getBlock() instanceof AxleBlock ab)
  544.                 ab.rotationStopped(state, this.getWorld(), this.getBlockPos().up(i));
  545.         }
  546.     }
  547.    
  548.     private void wasActive () {
  549.         return this.tracker.get(ACTIVEO).booleanValue();
  550.     }
  551.    
  552.     private void updateActivity () {
  553.         this.tracker.set(ACTIVEO, this.tracker.get(ACTIVE));
  554.         if (this.getWorld().getRegistryKey().getValue().toString() == "minecraft:the_end") {
  555.             this.tracker.set(ACTIVE, Boolean.FALSE);
  556.         } else if (!world.isSkyVisible(this.getBlockPos())) {
  557.             this.tracker.set(ACTIVE, Boolean.FALSE);
  558.         } else {
  559.             this.tracker.set(ACTIVE, Boolean.TRUE);
  560.         }
  561.     }
  562. }
  563.  
  564. public class WaterWheelEntity extends AbstractMechanicalPowerSupplierEntity {
  565.     public static final TrackedData<Direction> FACING = new TrackedData<Direction>(0, TrackedDataHandlerRegistry.FACING);
  566.     public static final TrackedData<Boolean> ACTIVEO = new TrackedData<Boolean>(0, TrackedDataHandlerRegistry.BOOLEAN);
  567.    
  568.     public WaterWheelEntity (World world) {
  569.         super (BTWEntityTypes.WATER_WHEEL, world);
  570.     }
  571.    
  572.     public void initDataTracker (DataTracker.Builder builder) {
  573.         this.tracker = builder.add(ACTIVE, Boolean.FALSE).add(ACTIVEO, Boolean.FALSE).add(FACING, Direction.SOUTH).build();
  574.     }
  575.    
  576.     protected void readCustomDataFromNbt (NbtCompound tag) {
  577.         super.readCustomDataFromNbt(tag);
  578.         this.tracker.set(FACING, Direction.byName(tag.getString("facing")));
  579.         this.tracker.set(ACTIVEO, Boolean.valueOf(tag.getBoolean("activeO")));
  580.     }
  581.    
  582.     protected void writeCustomDataToNbt (NbtCompound tag) {
  583.         super.writeCustomDataToNbt(tag);
  584.         tag.putString("facing", this.tracker != null ? this.tracker.get(FACING).asString() : "south");
  585.         tag.putBoolean("activeO", this.tracker != null && this.tracker.get(ACTIVEO).booleanValue());
  586.     }
  587.    
  588.     public void onHostAxleRemoved () {
  589.         this.dispatchRotationStopped();
  590.         this.remove(Entity.RemovalReason.DISCARDED);
  591.     }
  592.    
  593.     public void place (ItemPlacementContext ctx) {
  594.         BlockState targetState = this.getWorld().getBlockState(ctx.getBlockPos());
  595.         if (targetState.getBlock() instanceof AxleBlock ab) {
  596.             if (targetState.hasProperty(AxleBlock.DIRECTION)) this.tracker.set(FACING, targetState.getValue(AxleBlock.DIRECTION));
  597.             this.setPos(ctx.getBlockPos().getX() + 0.5, ctx.getBlockPos().getY() + 0.5, ctx.getBlockPos().getZ() + 0.5);
  598.         }
  599.     }
  600.    
  601.     public void tick () {
  602.         this.updateActivity();
  603.         if (this.isActive())
  604.             this.dispatchRotationTick();
  605.         else
  606.             if (this.wasActive())
  607.                 this.dispatchRotationStopped();
  608.     }
  609.    
  610.     public void dispatchRotationTick () {
  611.         BlockState state = this.getWorld().getBlockState(this.getBlockPos());
  612.         if (state.getBlock() instanceof AxleBlock ab)
  613.             ab.baseRotationTick(state, this.getWorld(), this.getBlockPos());
  614.     }
  615.    
  616.     public void dispatchRotationStopped () {
  617.         BlockState state = this.getWorld().getBlockState(this.getBlockPos());
  618.         if (state.getBlock() instanceof AxleBlock ab)
  619.             ab.rotationStopped(state, this.getWorld(), this.getBlockPos());
  620.     }
  621.    
  622.     private void wasActive () {
  623.         return this.tracker.get(ACTIVEO).booleanValue();
  624.     }
  625.    
  626.     private void updateActivity () {
  627.         this.tracker.set(ACTIVEO, this.tracker.get(ACTIVE));
  628.         this.tracker.set(ACTIVE, Boolean.valueOf(this.canBeActive()));
  629.     }
  630.    
  631.     private boolean canBeActive () {
  632.         int clockwiseCount = 0;
  633.         int counterclockwiseCount = 0;
  634.         BlockPos[] down = new BlockPos[] { this.getBlockPos().down(2).offset(2L, this.tracker.get(FACING).rotateYClockwise()), this.getBlockPos().down(2).offset(1L, this.tracker.get(FACING).rotateYClockwise()), this.getBlockPos().down(2), this.getBlockPos().down(2).offset(1L, this.tracker.get(FACING).rotateYCounterclockwise()), this.getBlockPos().down(2).offset(2L, this.tracker.get(FACING).rotateYCounterclockwise()) };
  635.         for (BlockPos p : down) {
  636.             if (this.getWorld().getFluidState(p).getType() != Fluids.WATER) continue;
  637.             Vec3 dir = this.getWorld().getFluidState(p).getVelocity(this.getWorld(), p);
  638.             double comp = dir.getComponentAlongAxis(this.tracker.get(FACING).rotateYClockwise().getAxis());
  639.             Direction aligned = Direction.from(this.tracker.get(FACING).rotateYClockwise().getAxis(), (Math.abs(comp) == comp) ? Direction.AxisDirection.POSITIVE : Direction.AxisDirection.NEGATIVE);
  640.             if (this.tracker.get(FACING).rotateYClockwise() == aligned)
  641.                 clockwiseCount++;
  642.             else
  643.                 counterclockwiseCount++;
  644.         }
  645.         BlockPos[] right = new BlockPos[] { this.getBlockPos().down(2).offset(2L, this.tracker.get(FACING).rotateYCounterclockwise()), this.getBlockPos().down(1).offset(2L, this.tracker.get(FACING).rotateYCounterclockwise()), this.getBlockPos().offset(2L, this.tracker.get(FACING).rotateYCounterclockwise()), this.getBlockPos().up(1).offset(2L, this.tracker.get(FACING).rotateYCounterclockwise()), this.getBlockPos().up(2).offset(2L, this.tracker.get(FACING).rotateYCounterclockwise()) };
  646.         for (BlockPos p : right) {
  647.             if (this.getWorld().getFluidState(p).getType() != Fluids.WATER) continue;
  648.             Vec3 dir = this.getWorld().getFluidState(p).getVelocity(this.getWorld(), p);
  649.             double comp = dir.getComponentAlongAxis(Direction.Axis.DOWN);
  650.             if (comp > 0)
  651.                 clockwiseCount++;
  652.         }
  653.         BlockPos[] up = new BlockPos[] { this.getBlockPos().up(2).offset(2L, this.tracker.get(FACING).rotateYClockwise()), this.getBlockPos().up(2).offset(1L, this.tracker.get(FACING).rotateYClockwise()), this.getBlockPos().up(2), this.getBlockPos().up(2).offset(1L, this.tracker.get(FACING).rotateYCounterclockwise()), this.getBlockPos().up(2).offset(2L, this.tracker.get(FACING).rotateYCounterclockwise()) };
  654.         for (BlockPos p : up) {
  655.             if (this.getWorld().getFluidState(p).getType() != Fluids.WATER) continue;
  656.             Vec3 dir = this.getWorld().getFluidState(p).getVelocity(this.getWorld(), p);
  657.             double comp = dir.getComponentAlongAxis(this.tracker.get(FACING).rotateYClockwise().getAxis());
  658.             Direction aligned = Direction.from(this.tracker.get(FACING).rotateYClockwise().getAxis(), (Math.abs(comp) == comp) ? Direction.AxisDirection.POSITIVE : Direction.AxisDirection.NEGATIVE);
  659.             if (this.tracker.get(FACING).rotateYClockwise() == aligned)
  660.                 counterclockwiseCount++;
  661.             else
  662.                 clockwiseCount++;
  663.         }
  664.         BlockPos[] left = new BlockPos[] { this.getBlockPos().down(2).offset(2L, this.tracker.get(FACING).rotateYClockwise()), this.getBlockPos().down(1).offset(2L, this.tracker.get(FACING).rotateYClockwise()), this.getBlockPos().offset(2L, this.tracker.get(FACING).rotateYClockwise()), this.getBlockPos().up(1).offset(2L, this.tracker.get(FACING).rotateYClockwise()), this.getBlockPos().up(2).offset(2L, this.tracker.get(FACING).rotateYClockwise()) };
  665.         for (BlockPos p : left) {
  666.             if (this.getWorld().getFluidState(p).getType() != Fluids.WATER) continue;
  667.             Vec3 dir = this.getWorld().getFluidState(p).getVelocity(this.getWorld(), p);
  668.             double comp = dir.getComponentAlongAxis(Direction.Axis.DOWN);
  669.             if (comp > 0)
  670.                 counterclockwiseCount++;
  671.         }
  672.         return clockwiseCount != counterclockwiseCount;
  673.     }
  674. }
  675.  
  676. public class FCHopperBlock extends Block implements MechanicalPowerBlock {
  677.     public static final FilterProperty FILTER = new FilterProperty("filter", Filter.class);
  678.    
  679.     public FCHopperBlock (AbstractBlock.Settings settings) {
  680.         super (settings);
  681.     }
  682.    
  683.     public void onSteppedOn (World world, BlockPos pos, BlockState state, Entity entity) {
  684.         if (entity instanceof ItemEntity ie) {
  685.             List<RegistryWrapper.Impl> impls = List.<RegistryWrapper.Impl>of()
  686.             Registries.ROOT.stream().forEach((t) -> {
  687.                 impls.add(t.getReadOnlyWrapper());
  688.             });
  689.             RecipeManager manager = new RecipeManager(RegistryWrapper.WrapperLookup.of(impls.stream()));
  690.             Optional<RecipeHolder<HopperFilteringRecipe>> maybeRecipe = manager.getFirstMatch(BTWRecipeTypes.HOPPER_FILTERING, ie.getItem(), world);
  691.             if (maybeRecipe.isPresent()) {
  692.                 HopperFilteringRecipe recipe = maybeRecipe.get().value();
  693.                 if (this.getFilter(state) == recipe.getFilter() && recipe.isValid(ie.getItem()) && (this.isActive() == recipe.requiresPower())) {
  694.                     if (recipe.isVolatile() && (ie.getItem().getCount() >= recipe.getStackSizeLimit()))
  695.                         world.createExplosion(null, new DamageSource(new RegistryEntry.Direct(new DamageType("better_than_wolves:damage_type.hopper_explosion.message", DamageScaling.ALWAYS, 0F))), new ExplosionBehavior(), pos.getX() + 0.5F, pos.getY() + 0.5F, pos.getZ() + 0.5F, 2.5F, true, World.ExplosionSourceType.BLOCK);
  696.                     Block.dropStack(world, pos, Direction.UP, recipe.assemble(ie.getItem(), RegistryWrapper.WrapperLookup.of(impls.stream())));
  697.                     if (recipe.hasAdditionalOutput())
  698.                         this.getOrCreateBlockEntity(world, pos).inventory.add(recipe.getAdditionalOutput(ie.getItem()));
  699.                     if (this.getFilter(state) == Filter.SOUL_SAND && world.getBlockState(pos.down()).getBlock() == BTWBlocks.URN) {
  700.                         world.setBlockState(pos.down(), Blocks.AIR.getDefaultState());
  701.                         Block.dropStack(world, pos.down, new ItemStack(BTWItems.SOUL_URN));
  702.                         world.playSound((Entity) null, pos.down(), SoundEvents.ENTITY_GHAST_AMBIENT, SoundCategory.BLOCKS, 0.8F, 1.5F);
  703.                     }
  704.                 }
  705.             } else if (this.getFilter(state) == null) {
  706.                 this.getOrCreateBlockEntity(world, pos).inventory.add(ie.getItem());
  707.                 ie.setItem(ItemStack.EMPTY);
  708.             }
  709.         }
  710.     }
  711.    
  712.     public void onUse (BlockState state, World world, BlockPos pos, PlayerEntity player, BlockHitResult hit) {
  713.         if (world.isClientSide())
  714.             MinecraftClient.getInstance().setScreen(this.getOrCreateBlockEntity(world, pos).createScreen());
  715.     }
  716.    
  717.     public List<ItemStack> getDroppedStacks (BlockState state, LootContextParameterSet$Builder builder) {
  718.         return super.getDroppedStacks(state, builder).add(this.getFilter(state) == null ? new ItemStack(this.getFilter(state)) : ItemStack.EMPTY);
  719.     }
  720.    
  721.     protected VoxelShape getRaycastShape (BlockState state, BlockView world, BlockPos pos) {
  722.         return VoxelShapes.union(VoxelShapes.cuboid(0.0, 6.0, 0.0, 16.0, 16.0, 16.0), VoxelShapes.cuboid(5.0, 0.0, 5.0, 11.0, 6.0, 11.0));
  723.     }
  724.    
  725.     protected VoxelShape getOutlineShape (BlockState state, BlockView world, BlockPos pos, ShapeContext context) {
  726.         return VoxelShapes.union(VoxelShapes.cuboid(0.0, 6.0, 0.0, 16.0, 16.0, 16.0), VoxelShapes.cuboid(5.0, 0.0, 5.0, 11.0, 6.0, 11.0));
  727.     }
  728.    
  729.     protected VoxelShape getCollisionShape (BlockState state, BlockView world, BlockPos pos, ShapeContext context) {
  730.         return VoxelShapes.union(VoxelShapes.cuboid(0.0, 6.0, 0.0, 2.0, 16.0, 16.0), VoxelShapes.cuboid(14.0, 6.0, 0.0, 16.0, 16.0, 16.0), VoxelShapes.cuboid(2.0, 6.0, 0.0, 14.0, 16.0, 2.0), VoxelShapes.cuboid(2.0, 6.0, 14.0, 14.0, 16.0, 16.0), VoxelShapes.cuboid(5.0, 0.0, 5.0, 11.0, 6.0, 11.0));
  731.     }
  732.    
  733.     public Filter getFilter (BlockState state) {
  734.         if (state.hasProperty(FILTER))
  735.             return state.getValue(FILTER);
  736.         else
  737.             return null;
  738.     }
  739.    
  740.     public BlockState setFilter (BlockState state, Filter filter) {
  741.         return state.withIfExists(FILTER, filter);
  742.     }
  743.    
  744.     public FCHopperBlockEntity getOrCreateBlockEntity (World world, BlockPos pos) {
  745.         if (world.getBlockEntity(pos) instanceof FCHopperBlockEntity fchbe) {
  746.             return fchbe;
  747.         } else {
  748.             FCHopperBlockEntity hopper = new FCHopperBlockEntity(pos, world.getBlockState(pos));
  749.             hopper.setWorld(world);
  750.             return hopper;
  751.         }
  752.     }
  753.    
  754.     public boolean recievesPower (World world, BlockPos pos, Direction toDir) {
  755.         return toDir.getAxis() != Direction.Axis.Y;
  756.     }
  757.    
  758.     public boolean isSimple () {
  759.         return false;
  760.     }
  761.    
  762.     public void rotationTick (BlockState state, World world, BlockPos pos) {
  763.         this.getOrCreateBlockEntity(world, pos).rotationTick();
  764.     }
  765.    
  766.     public static class Filter implements ItemLike, Comparable<Filter> {
  767.         public static final Filter SOUL_SAND = new Filter(Items.SOUL_SAND, 0);
  768.         public static final Filter WICKER_PANE = new Filter(BTWItems.WICKER_PANE, 1);
  769.         public static final Filter SLATS = new Filter(BTWItems.SLATS, 2);
  770.         public static final Filter GRATE = new Filter(BTWItems.GRATE, 3);
  771.         public static final Filter OAK_TRAPDOOR = new Filter(Items.OAK_TRAPDOOR, 4);
  772.         public static final Filter IRON_BARS = new Filter(Items.IRON_BARS, 5);
  773.         public static final Filter LADDER = new Filter(Items.LADDER, 6);
  774.        
  775.         private final ItemLike itemLike;
  776.         private final int index;
  777.        
  778.         private Filter (ItemLike item, int order) {
  779.             this.itemLike = item;
  780.             this.index = order;
  781.         }
  782.        
  783.         public static Filter get (int i) {
  784.             switch (i) {
  785.                 case 0:
  786.                     return SOUL_SAND;
  787.                 case 1:
  788.                     return WICKER_PANE;
  789.                 case 2:
  790.                     return SLATS;
  791.                 case 3:
  792.                     return GRATE;
  793.                 case 4:
  794.                     return OAK_TRAPDOOR;
  795.                 case 5:
  796.                     return IRON_BARS;
  797.                 case 6:
  798.                     return LADDER;
  799.                 default:
  800.                     return null;
  801.             }
  802.         }
  803.        
  804.         public Item asItem () {
  805.             return this.itemLike.asItem();
  806.         }
  807.        
  808.         public int getOrder () {
  809.             return this.index;
  810.         }
  811.        
  812.         public int compareTo (Filter other) {
  813.             return this.getOrder() - other.getOrder();
  814.         }
  815.     }
  816.    
  817.     public class FilterProperty extends Property<Filter> {
  818.         public Collection<Filter> getPossibleValues () {
  819.             return List.<Filter>of(Filter.SOUL_SAND, Filter.WICKER_PANE, Filter.SLATS, Filter.GRATE, Filter.TRAPDOOR, Filter.IRON_BARS, Filter.LADDER);
  820.         }
  821.        
  822.         public String getName (Filter value) {
  823.             return Registries.ITEM.getId(value.asItem()).toString();
  824.         }
  825.        
  826.         public Optional<Filter> parse (String name) {
  827.             if (Identifier.parse(name).toString() == "minecraft:soul_sand")
  828.                 return Optional.<Filter>of(Filter.SOUL_SAND);
  829.             else if (name == "better_than_wolves:wicker_pane")
  830.                 return Optional.<Filter>of(Filter.WICKER_PANE);
  831.             else if (name == "better_than_wolves:slats")
  832.                 return Optional.<Filter>of(Filter.SLATS);
  833.             else if (name == "better_than_wolves:grate")
  834.                 return Optional.<Filter>of(Filter.GRATE);
  835.             else if (Identifier.parse(name).toString() == "minecraft:oak_trapdoor")
  836.                 return Optional.<Filter>of(Filter.TRAPDOOR);
  837.             else if (Identifier.parse(name).toString() == "minecraft:iron_bars")
  838.                 return Optional.<Filter>of(Filter.IRON_BARS);
  839.             else if (Identifier.parse(name).toString() == "minecraft:ladder")
  840.                 return Optional.<Filter>of(Filter.LADDER);
  841.             else
  842.                 return Optional.<Filter>empty();
  843.         }
  844.     }
  845. }
  846.  
  847. public final class FCHopperBlockEntity extends BlockEntity implements Container {
  848.     List<ItemStack> inventory = List.<ItemStack>of();
  849.    
  850.     public FCHopperBlockEntity (BlockPos pos, BlockState state) {
  851.         super (BTWBlockEntityTypes.HOPPER, pos, state);
  852.     }
  853.    
  854.     public Screen createScreen () {
  855.         return this.new Screen();
  856.     }
  857.    
  858.     public void rotationTick () {
  859.         if (this.getWorld().getBlockState(this.getBlockPos().down()).isReplaceable()) {
  860.             int index = this.getWorld().random.nextInt(17);
  861.             int count = Math.max(this.inventory.get(index).getCount(), 8);
  862.             Block.dropStack(world, pos, new ItemStack(this.inventory.get(index).getItem(), count), Direction.DOWN);
  863.             this.inventory.get(index).shrink(count);
  864.         } else if (this.getWorld().getBlockEntity(this.getBlockPos().down()) instanceof Inventory i) {
  865.             int index = this.getWorld().random.nextInt(17);
  866.             int amt = Math.min(this.inventory.get(index).getCount(), 8);
  867.             for (int j = 0; j <= i.getContainerSize(); j++) {
  868.                 if (i.getStack(j).getItem() == this.inventory.get(index).getItem()) {
  869.                     if (i.getStack(j).getCount() <= i.getStack(j).getMaxStackSize() - amt) {
  870.                         i.setStack(j, i.getStack(j).copy().grow(amt));
  871.                         break;
  872.                     } else {
  873.                         int amount = i.getStack(j).getCount() + amt - i.getStack(j).getMaxStackSize();
  874.                         i.setStack(j, i.getStack(j).copy().grow(amount));
  875.                         amt -= amount;
  876.                     }
  877.                 }
  878.             }
  879.         }
  880.     }
  881. }
  882.  
  883. public class HopperFilteringRecipe implements Recipe<SingleStackRecipeInput> {
  884.     private final SingleStackRecipeInput input;
  885.     private final ItemStack output;
  886.    
  887.     public HopperFilteringRecipe (Item in, ItemStack out) {
  888.         this.input = new SingleStackRecipeInput(new ItemStack(in));
  889.         this.output = out;
  890.     }
  891.    
  892.     public boolean matches (SingleStackRecipeInput i, World world) {
  893.         return i == this.input;
  894.     }
  895.    
  896.     public ItemStack assemble (SingleStackRecipeInput i, RegistryWrapper.WrapperLookup wrapper) {
  897.         return this.output;
  898.     }
  899.    
  900.     public boolean fits (int width, int height) {
  901.         return width >= 1 && height >= 1;
  902.     }
  903.    
  904.     public ItemStack getResultItem (RegistryWrapper.WrapperLookup wrapper) {
  905.         return this.output;
  906.     }
  907.    
  908.     public RecipeSerializer<MillingRecipe> getSerializer () {
  909.         return new Serializer();
  910.     }
  911.    
  912.     public RecipeType<MillingRecipe> getType () {
  913.         return BTWRecipeTypes.HOPPER_FILTERING;
  914.     }
  915.    
  916.     public ItemStack getInput () {
  917.         return this.input.item();
  918.     }
  919.    
  920.     public ItemStack getOutput () {
  921.         return this.output;
  922.     }
  923.    
  924.     public static class Serializer implements RecipeSerializer<MillingRecipe> {
  925.         public Serializer () {}
  926.        
  927.         public MapCodec<MillingRecipe> codec () {
  928.             return new MapCodec<MillingRecipe> () {
  929.                 public <T> Stream<T> keys (DynamicOps<T> ops) {
  930.                     return List.<T>of(ops.createString("input"), ops.createString("output"));
  931.                 }
  932.                
  933.                 public <T> KeyCompressor<T> compressor (DynamicOps<T> ops) {
  934.                     return new KeyCompressor<T>(ops, this.keys(ops));
  935.                 }
  936.                
  937.                 public <T> DataResult<MillingRecipe> decode (DynamicOps<T> ops, MapLike<T> map) {
  938.                     try {
  939.                         MapLike<T> inputA = ops.getMap(map.get("input")).result().getOrThrow();
  940.                         ItemStack input = new ItemStack(Registries.ITEM.get(Identifier.of(ops.getString(inputA.get("item")).result().getOrThrow())), ops.getNumberValue(inputA.get("count"), Integer.valueOf(1)).intValue());
  941.                         MapLike<T> outputA = ops.getMap(map.get("output")).result().getOrThrow();
  942.                         ItemStack output = new ItemStack(Registries.ITEM.get(Identifier.of(ops.getString(outputA.get("item")).result().getOrThrow())), ops.getNumberValue(outputA.get("count"), Integer.valueOf(1)).intValue());
  943.                         return DataResult.success(new MillingRecipe(input, output));
  944.                     } catch {
  945.                         return DataResult.error("");
  946.                     }
  947.                 }
  948.                
  949.                 public <T> RecordBuilder<T> encode(MillingRecipe input, DynamicOps<T> ops, RecordBuilder<T> prefix) {
  950.                     Map<T, T> inputMap = Map.<T, T>of();
  951.                     inputMap.put(ops.createString("item"), ops.createString(Registries.ITEM.getId(input.getOutput().getItem()).toString()));
  952.                     inputMap.put(ops.createString("count"), ops.createInt(input.getInput().getCount()));
  953.                     prefix.add("input", ops.createMap(MapLike.of(inputMap, ops)));
  954.                     Map<T, T> output = Map.<T, T>of();
  955.                     output.put(ops.createString("item"), ops.createString(Registries.ITEM.getId(input.getOutput().getItem()).toString()));
  956.                     output.put(ops.createString("count"), ops.createInt(input.getOutput().getCount()));
  957.                     prefix.add("output", ops.createMap(MapLike.of(output, ops)));
  958.                     return prefix
  959.                 }
  960.             }
  961.         }
  962.        
  963.         public PacketCodec<RegistryByteBuf, T> packetCodec () {
  964.             return PacketCodecs.codec(this.codec().codec());
  965.         }
  966.     }
  967. }
  968.  
  969. public class MillStoneBlock extends Block implements MechanicalPowerBlock {
  970.     public MillStoneBlock (AbstractBlock.Settings settings) {
  971.         super (settings);
  972.     }
  973.    
  974.     public ActionResult onUse (BlockState state, World world, BlockPos pos, PlayerEntity player, BlockHitResult hit) {
  975.         if (this.getOrCreateBlockEntity(world, pos).hasItem()) {
  976.             if (!player.getInventory().insertStack(new ItemStack(this.getOrCreateBlockEntity(world, pos).getItem())))
  977.                 Block.dropStack(world, pos, hit.getDirection(), new ItemStack(this.getOrCreateBlockEntity(world, pos).getItem()));
  978.             return ActionResult.sidedSuccess(true);
  979.         } else {
  980.             return super.onUse(state, world, pos, player, hit);
  981.         }
  982.     }
  983.    
  984.     public ItemActionResult onUseWithItem (ItemStack stack, BlockState state, World world, BlockPos pos, PlayerEntity player, Hand hand, BlockHitResult hit) {
  985.         if (this.getOrCreateBlockEntity(world, pos).hasItem()) {
  986.             if (!player.getInventory().insertStack(new ItemStack(this.getOrCreateBlockEntity(world, pos).getItem())))
  987.                 Block.dropStack(world, pos, hit.getDirection(), new ItemStack(this.getOrCreateBlockEntity(world, pos).getItem()));
  988.             this.getOrCreateBlockEntity(world, pos).setItem(null);
  989.             return ItemActionResult.sidedSuccess(true);
  990.         } else {
  991.             player.setStackInHand(hand, player.getStackInHand(hand).shrink(1));
  992.             this.getOrCreateBlockEntity(world, pos).setItem(player.getStackInHand(hand).getItem());
  993.         }
  994.     }
  995.    
  996.     public boolean recievesPower (World world, BlockPos pos, Direction toDir) {
  997.         return toDir.getAxis() == Direction.Axis.Y;
  998.     }
  999.    
  1000.     public boolean isSimple () {
  1001.         return true;
  1002.     }
  1003.    
  1004.     public void rotationTick (BlockState state, World world, BlockPos pos) {
  1005.         if (this.getOrCreateBlockEntity(world, pos).hasItem())
  1006.             if (this.getOrCreateBlockEntity(world, pos).processItem())
  1007.                 Block.dropStack(world, pos, Direction.getRandom(world.getRandom()), new ItemStack(this.getOrCreateBlockEntity(world, pos).getItem()));
  1008.         state.withIfExists(Properties.ACTIVE, Boolean.valueOf(this.getOrCreateBlockEntity(world, pos).hasItem()));
  1009.         if (world.getRandom().nextInt(8) == 0) world.playSound((Entity) null, BlockPos pos, SoundEvents.ENTITY_MINECART_RIDING, SoundCategory.BLOCKS, 0.6F, (float) ((world.getRandom().nextInt(2) / 10) + 0.9));
  1010.     }
  1011.    
  1012.     private MillStoneBlockEntity getOrCreateBlockEntity (World world, BlockPos pos) {
  1013.         if (world.getBlockEntity(pos) instanceof MillStoneBlockEntity msbe) {
  1014.             return msbe;
  1015.         } else {
  1016.             MillStoneBlockEntity entity = new MillStoneBlockEntity(pos, world.getBlockState(pos));
  1017.             entity.setWorld(world);
  1018.             return entity;
  1019.         }
  1020.     }
  1021. }
  1022.  
  1023. public final class MillStoneBlockEntity extends BlockEntity {
  1024.     private Item item = null;
  1025.     private int processingTicks = 0;
  1026.    
  1027.     public MillStoneBlockEntity (BlockPos pos, BlockState state) {
  1028.         super (BTWBlockEntityTypes.MILL_STONE, pos, state);
  1029.     }
  1030.    
  1031.     public boolean hasItem () {
  1032.         return this.item != null;
  1033.     }
  1034.    
  1035.     public Item getItem () {
  1036.         return this.item;
  1037.     }
  1038.    
  1039.     public void setItem (Item item) {
  1040.         this.item = item;
  1041.     }
  1042.    
  1043.     public void processItem () {
  1044.         if (this.hasItem()) {
  1045.             if (this.processingTicks >= 200 && this.result != null) {
  1046.                 this.processingTicks = 0;
  1047.                 BlockEntity below = this.getWorld().getBlockEntity(this.getBlockPos().down());
  1048.                 if (below instanceof Hopper || below instanceof FCHopperBlockEntity) {
  1049.                     Container be = (Container)below;
  1050.                     boolean found = false;
  1051.                     for (int i = 0; i <= be.size(); i++) {
  1052.                         if (be.getStack(i) == ItemStack.EMPTY) {
  1053.                             be.getStack(i).grow(1);
  1054.                             found = true;
  1055.                             break;
  1056.                         } else if (be.getStack(i).getItem() == this.result && be.getStack(i).getCount() < be.getStack(i).getMaxStackSize()) {
  1057.                             be.setStack(i, new ItemStack(this.result));
  1058.                         }
  1059.                     }
  1060.                     if (!found) Block.dropStack(this.getWorld(), this.getBlockPos(), this.getRandomAvailableDirection(), new ItemStack(this.result));
  1061.                 } else {
  1062.                     Block.dropStack(this.getWorld(), this.getBlockPos(), this.getRandomAvailableDirection(), new ItemStack(this.result));
  1063.                 }
  1064.                 this.setItem(null);
  1065.                 this.result = null;
  1066.             } else if (this.processingTicks > 0) {
  1067.                 this.processingTicks++;
  1068.             } else {
  1069.                 this.updateNextItem();
  1070.             }
  1071.         } else {
  1072.             this.processingTicks = 0;
  1073.             this.result = null;
  1074.         }
  1075.     }
  1076.    
  1077.     private void updateNextItem () {
  1078.         if (this.hasItem()) {
  1079.             List<RegistryWrapper.Impl> impls = List.<RegistryWrapper.Impl>of()
  1080.             Registries.ROOT.stream().forEach((t) -> {
  1081.                 impls.add(t.getReadOnlyWrapper());
  1082.             });
  1083.             RecipeManager manager = new RecipeManager(RegistryWrapper.WrapperLookup.of(impls.stream()));
  1084.             Optional<RecipeHolder<MillingRecipe>> maybeRecipe = manager.getFirstMatch(BTWRecipeTypes.MILLING, this.getItem(), world);
  1085.             if (maybeRecipe.isPresent())
  1086.                 this.result = maybeRecipe.get().value().assemble(this.getItem(), RegistryWrapper.WrapperLookup.of(impls.stream()));
  1087.             else
  1088.                 this.result = null;
  1089.         } else {
  1090.             this.result = null;
  1091.         }
  1092.     }
  1093.    
  1094.     private Direction getRandomAvailableDirection () {
  1095.         for (Direction d : Direction.allShuffled()) {
  1096.             if (d.getAxis() == Direction.Axis.Y) continue;
  1097.             if (!this.getWorld().getBlockState(this.getBlockPos().offset(d)).isReplaceable()) continue;
  1098.             return d;
  1099.         }
  1100.     }
  1101. }
  1102.  
  1103. public class MillingRecipe implements Recipe<SingleStackRecipeInput> {
  1104.     private final SingleStackRecipeInput input;
  1105.     private final ItemStack output;
  1106.    
  1107.     public MillingRecipe (Item in, ItemStack out) {
  1108.         this.input = new SingleStackRecipeInput(new ItemStack(in));
  1109.         this.output = out;
  1110.     }
  1111.    
  1112.     public boolean matches (SingleStackRecipeInput i, World world) {
  1113.         return i == this.input;
  1114.     }
  1115.    
  1116.     public ItemStack assemble (SingleStackRecipeInput i, RegistryWrapper.WrapperLookup wrapper) {
  1117.         return this.output;
  1118.     }
  1119.    
  1120.     public boolean fits (int width, int height) {
  1121.         return width >= 1 && height >= 1;
  1122.     }
  1123.    
  1124.     public ItemStack getResultItem (RegistryWrapper.WrapperLookup wrapper) {
  1125.         return this.output;
  1126.     }
  1127.    
  1128.     public RecipeSerializer<MillingRecipe> getSerializer () {
  1129.         return new Serializer();
  1130.     }
  1131.    
  1132.     public RecipeType<MillingRecipe> getType () {
  1133.         return BTWRecipeTypes.MILLING;
  1134.     }
  1135.    
  1136.     public Item getInput () {
  1137.         return this.input.item().getItem();
  1138.     }
  1139.    
  1140.     public ItemStack getOutput () {
  1141.         return this.output;
  1142.     }
  1143.    
  1144.     public static class Serializer implements RecipeSerializer<MillingRecipe> {
  1145.         public Serializer () {}
  1146.        
  1147.         public MapCodec<MillingRecipe> codec () {
  1148.             return new MapCodec<MillingRecipe> () {
  1149.                 public <T> Stream<T> keys (DynamicOps<T> ops) {
  1150.                     return List.<T>of(ops.createString("input"), ops.createString("output"));
  1151.                 }
  1152.                
  1153.                 public <T> KeyCompressor<T> compressor (DynamicOps<T> ops) {
  1154.                     return new KeyCompressor<T>(ops, this.keys(ops));
  1155.                 }
  1156.                
  1157.                 public <T> DataResult<MillingRecipe> decode (DynamicOps<T> ops, MapLike<T> map) {
  1158.                     try {
  1159.                         Item input = Registries.ITEM.get(Identifier.of(ops.getString(map.get("input")).result().getOrThrow()));
  1160.                         MapLike<T> outputA = ops.getMap(map.get("output")).result().getOrThrow();
  1161.                         ItemStack output = new ItemStack(Registries.ITEM.get(Identifier.of(ops.getString(outputA.get("item")).result().getOrThrow())), ops.getNumberValue(outputA.get("count"), Integer.valueOf(1)).intValue());
  1162.                         return DataResult.success(new MillingRecipe(input, output));
  1163.                     } catch {
  1164.                         return DataResult.error("");
  1165.                     }
  1166.                 }
  1167.                
  1168.                 public <T> RecordBuilder<T> encode(MillingRecipe input, DynamicOps<T> ops, RecordBuilder<T> prefix) {
  1169.                     prefix.add("input", ops.createString(Registries.ITEM.getId(input.getInput())));
  1170.                     Map<T, T> output = Map.<T, T>of();
  1171.                     preInput.put(ops.createString("item"), ops.createString(Registries.ITEM.getId(input.getOutput().getItem()).toString()));
  1172.                     preInput.put(ops.createString("count"), ops.createInt(input.getOutput().getCount()));
  1173.                     prefix.add("output", ops.createMap(MapLike.of(output, ops)));
  1174.                 }
  1175.             }
  1176.         }
  1177.        
  1178.         public PacketCodec<RegistryByteBuf, T> packetCodec () {
  1179.             return PacketCodecs.codec(this.codec().codec());
  1180.         }
  1181.     }
  1182. }
  1183.  
  1184. public class PulleyBlock extends BlockWithEntity implements MechanicalPowerBlock {
  1185.     public PulleyBlock (AbstractBlock.Settings settings) {
  1186.         super (settings);
  1187.     }
  1188.    
  1189.     public MapCodec<PulleyBlock> getCodec () {
  1190.         return AbstractBlock.simpleCodec(PulleyBlock::new);
  1191.     }
  1192.    
  1193.     public PulleyBlockEntity createBlockEntity (BlockState state, BlockPos pos) {
  1194.         return new PulleyBlockEntity(state, pos);
  1195.     }
  1196.    
  1197.     public boolean recievesPower (World world, BlockPos pos, Direction toDir) {
  1198.         return toDir.getAxis() != Direction.Axis.Y;
  1199.     }
  1200.    
  1201.     public boolean isSimple () {
  1202.         return false;
  1203.     }
  1204.    
  1205.     public void rotationTick (BlockState state, World world, BlockPos pos) {}
  1206.    
  1207.     public void getTicker (World world, BlockState state, BlockEntityType<PulleyBlockEntity> type) {
  1208.         return (w, p, s, e) -> { e.tick(); };
  1209.     }
  1210. }
  1211.  
  1212. public class PulleyBlockEntity extends BlockEntity {
  1213.     public PulleyBlockEntity (BlockState state, BlockPos pos) {
  1214.         super (BTWBlockEntityTypes.PULLEY, state, pos);
  1215.     }
  1216.    
  1217.     public void tick () {
  1218.         List<AnchorEntity> eList = world.getEntitiesByClass(AnchorEntity.class, new Box(this.getLowestRopeBelow()));
  1219.         if (eList.size() > 1) {
  1220.             for (AnchorEntity e : eList.subList(1, eList.size() - 1))
  1221.                 e.remove(Entity.RemovalReason.DISCARDED);
  1222.         }
  1223.         if (this.getWorld().getBlockState(this.getBlockPos().up()).getWeakRedstonePower(this.getWorld(), this.getBlockPos().up(), Direction.DOWN) > 0)
  1224.             this.getBlockState().withIfExists(Properties.DISARMED, Boolean.TRUE);
  1225.         else
  1226.             this.getBlockState().withIfExists(Properties.DISARMED, Boolean.FALSE);
  1227.         if (eList.size() >= 1 && this.getBlockState().hasProperty(Properties.DISARMED) && !this.getBlockState().getValue(Properties.DISARMED).booleanValue()) {
  1228.             if (this.getBlockState().hasProperty(Properties.ACTIVE) && this.getBlockState().getValue(Properties.ACTIVE).booleanValue()) {
  1229.                 eList.getFirst().moveUp();
  1230.             } else {
  1231.                 eList.getFirst().moveDown();
  1232.             }
  1233.         }
  1234.     }
  1235.    
  1236.     private BlockPos getLowestRopeBelow () {
  1237.         BlockPos pos = this.getBlockPos();
  1238.         do {
  1239.             pos = pos.down();
  1240.         } while (world.getBlockState(pos).getBlock() instanceof RopeBlock);
  1241.         return pos.up();
  1242.     }
  1243. }
  1244.  
  1245. public class SawBlock extends FacingBlock implements MechanicalPowerBlock {
  1246.     public static final IntegerProperty SAW_PROGRESS = IntegerProperty.of("saw_progress", 0, 20);
  1247.    
  1248.     public SawBlock (AbstractBlock.Settings settings) {
  1249.         super (settings);
  1250.         this.setDefaultState(this.getStateManager().getDefaultState().with(FacingBlock.FACING, Direction.DOWN));
  1251.     }
  1252.    
  1253.     public BlockState getPlacementState (ItemPlacementContext ctx) {
  1254.         return this.getDefaultState().withIfExists(FacingBlock.FACING, ctx.getSide());
  1255.     }
  1256.    
  1257.     protected VoxelShape getRaycastShape (BlockState state, BlockView world, BlockPos pos) {
  1258.         return this.getShape(state.hasProperty(DIRECTION) ? state.getValue(DIRECTION) : Direction.DOWN);
  1259.     }
  1260.    
  1261.     protected VoxelShape getOutlineShape (BlockState state, BlockView world, BlockPos pos, ShapeContext context) {
  1262.         return this.getShape(state.hasProperty(DIRECTION) ? state.getValue(DIRECTION) : Direction.DOWN);
  1263.     }
  1264.    
  1265.     protected VoxelShape getCollisionShape (BlockState state, BlockView world, BlockPos pos, ShapeContext context) {
  1266.         return this.getShape(state.hasProperty(DIRECTION) ? state.getValue(DIRECTION) : Direction.DOWN);
  1267.     }
  1268.    
  1269.     private VoxelShape getShape (Direction dir) {
  1270.         if (dir == Direction.UP)
  1271.             return VoxelShapes.cuboid(0.0, 0.0, 0.0, 16.0, 12.0, 16.0);
  1272.         if (dir == Direction.DOWN)
  1273.             return VoxelShapes.cuboid(0.0, 4.0, 0.0, 16.0, 16.0, 16.0);
  1274.         if (dir == Direction.SOUTH)
  1275.             return VoxelShapes.cuboid(0.0, 0.0, 0.0, 16.0, 16.0, 12.0);
  1276.         if (dir == Direction.EAST)
  1277.             return VoxelShapes.cuboid(0.0, 0.0, 0.0, 12.0, 16.0, 16.0);
  1278.         if (dir == Direction.NORTH)
  1279.             return VoxelShapes.cuboid(0.0, 0.0, 4.0, 16.0, 16.0, 16.0);
  1280.         if (dir == Direction.WEST)
  1281.             return VoxelShapes.cuboid(4.0, 0.0, 0.0, 16.0, 16.0, 16.0);
  1282.         return VoxelShapes.fullCube();
  1283.     }
  1284.    
  1285.     public boolean recievesPower (World world, BlockPos pos, Direction toDir) {
  1286.         return world.getBlockState(pos).hasProperty(FacingBlock.FACING) && toDir != world.getBlockState(pos).getValue(FacingBlock.FACING);
  1287.     }
  1288.    
  1289.     public boolean isSimple () {
  1290.         return false;
  1291.     }
  1292.    
  1293.     public void rotationTick (BlockState state, World world, BlockPos pos) {
  1294.         if (!state.hasProperty(FacingBlock.FACING)) return;
  1295.         if (!state.hasProperty(SAW_PROGRESS)) return;
  1296.         if (state.getValue(SAW_PROGRESS).intValue() == 20) {
  1297.             state.setValue(SAW_PROGRESS, Integer.valueOf(0));
  1298.             this.postTimeout(state, world, pos);
  1299.         } else {
  1300.             state.setValue(SAW_PROGRESS, Integer.valueOf(state.getValue(SAW_PROGRESS).intValue() + 1));
  1301.         }
  1302.     }
  1303.    
  1304.     private void postTimeout (BlockState state, World world, BlockPos pos) {
  1305.         if (world.getBlockState(pos.offset(state.getValue(FacingBlock.FACING))).isIn(TagKey.<Block>of(Registries.BLOCK.getKey(), Identifier.of("better_than_wolves", "breaks_saw")))) this.breakSaw(state, world, pos);
  1306.         List<RegistryWrapper.Impl> impls = List.<RegistryWrapper.Impl>of();
  1307.         Registries.ROOT.stream().forEach((t) -> {
  1308.             impls.add(t.getReadOnlyWrapper());
  1309.         });
  1310.         RecipeManager manager = new RecipeManager(RegistryWrapper.WrapperLookup.of(impls.stream()));
  1311.         Optional<RecipeHolder<SawCuttingRecipe>> maybeRecipe = manager.getFirstMatch(BTWRecipeTypes.SAW_CUTTING, new SingleItemRecipeInput(new ItemStack(world.getBlockState(pos.offset(state.getValue(FacingBlock.FACING))).getBlock())), world);
  1312.         if (maybeRecipe.isPresent())
  1313.             this.dropFromBlock(world, pos.offset(state.getValue(FacingBlock.FACING))), maybeRecipe.get().value().getResult(world.getBlockState(pos.offset(state.getValue(FacingBlock.FACING))).getBlock()), RegistryWrapper.WrapperLookup.of(impls.stream()));
  1314.     }
  1315.    
  1316.     private void breakSaw (BlockState state, World world, BlockPos pos) {
  1317.         world.setBlockState(pos, Blocks.AIR.getDefaultState());
  1318.         if (world.isClientSide()) world.addBlockBreakParticles(pos, state);
  1319.         world.playSound((Entity) null, pos, SoundEvents.ENTITY_ZOMBIE_BREAK_WOODEN_DOOR, SoundCategory.BLOCKS, 1.0F, 1.0F);
  1320.         Block.dropStack(world, pos, new ItemStack(BTWItems.GEAR));
  1321.         Block.dropStack(world, pos, new ItemStack(Items.IRON_INGOT, 2));
  1322.         Block.dropStack(world, pos, new ItemStack(Items.IRON_NUGGET, 4));
  1323.         Block.dropStack(world, pos, new ItemStack(BTWItems.STRAP, 3));
  1324.     }
  1325.    
  1326.     private void dropFromBlock (World world, BlockPos pos, List<ItemStack> items) {
  1327.         world.setBlockState(pos, Blocks.AIR.getDefaultState());
  1328.         if (world.isClientSide()) world.addBlockBreakParticles(pos, state);
  1329.         world.playSound((Entity) null, pos, world.getBlockState(pos).getBlock().getSettings().getSoundGroup(world.getBlockState(pos)).getBreakSound(), SoundCategory.BLOCKS, 1.0F, 1.0F);
  1330.         for (ItemStack i : items)
  1331.             Block.dropStack(world, pos, i);
  1332.     }
  1333. }
  1334.  
  1335. public class TurntableBlock extends Block implements MechanicalPowerBlock {
  1336.     public static final IntProperty TURNTABLE_TIMER = IntProperty.of("turntable_timer", 0, 40);
  1337.     public static final IntProperty TURNTABLE_SPEED = IntProperty.of("turntable_speed", 0, 3);
  1338.    
  1339.     public TurntableBlock (AbstractBlock.Settings settings) {
  1340.         super (settings);
  1341.     }
  1342.    
  1343.     public boolean recievesPower (World world, BlockPos pos, Direction toDir) {
  1344.         return toDir == Direction.DOWN;
  1345.     }
  1346.    
  1347.     public boolean isSimple () {
  1348.         return false;
  1349.     }
  1350.    
  1351.     public void rotationTick (BlockState state, World world, BlockPos pos) {
  1352.         if (!state.hasProperty(TURNTABLE_TIMER) || !state.hasProperty(TURNTABLE_SPEED)) return;
  1353.         state.with(TURNTABLE_TIMER, Integer.valueOf(state.getValue(TURNTABLE_TIMER).intValue() + 1));
  1354.         if (state.getValue(TURNTABLE_TIMER).intValue() >= Math.pow(2, state.getValue(TURNTABLE_SPEED).intValue()) * 10) {
  1355.             state.with(TURNTABLE_TIMER, Integer.valueOf(0));
  1356.             if (this.isPowered(world, pos))
  1357.                 this.turnInverse(world, pos.up());
  1358.             else
  1359.                 this.turn(world, pos.up());
  1360.         }
  1361.     }
  1362.    
  1363.     private void turn (World world, BlockPos pos) {
  1364.         world.setBlockState(pos, world.getBlockState(pos).rotate(BlockRotation.COUNTERCLOCKWISE_90));
  1365.         world.setBlockState(pos.up(), world.getBlockState(pos.up()).rotate(BlockRotation.COUNTERCLOCKWISE_90));
  1366.         ArrayList<Consumer<Object>> funcs = new ArrayList<Consumer<Object>>();
  1367.         for (Direction d : Direction.stream().collect(Collectors.asList())) {
  1368.             if (d.getAxis() == Direction.Axis.Y) continue;
  1369.             if (!world.getBlockState(pos.offset(d)).hasProperty(Properties.FACING)) continue;
  1370.             if (world.getBlockState(pos.offset(d)).getValue(Properties.FACING) != d) continue;
  1371.             BlockState state = world.getBlockState(pos.offset(d));
  1372.             funcs.add((obj) -> { world.setBlockState(pos.offset(d.rotateYCounterclockwise()), state.rotate(BlockRotation.COUNTERCLOCKWISE_90)); });
  1373.         }
  1374.         for (Consumer<Object> c : funcs) c.apply(null);
  1375.     }
  1376.    
  1377.     private void turnInverse (World world, BlockPos pos) {
  1378.         world.setBlockState(pos, world.getBlockState(pos).rotate(BlockRotation.CLOCKWISE_90));
  1379.         world.setBlockState(pos.up(), world.getBlockState(pos.up()).rotate(BlockRotation.CLOCKWISE_90));
  1380.         ArrayList<Consumer<Object>> funcs = new ArrayList<Consumer<Object>>();
  1381.         for (Direction d : Direction.stream().collect(Collectors.asList())) {
  1382.             if (d.getAxis() == Direction.Axis.Y) continue;
  1383.             if (!world.getBlockState(pos.offset(d)).hasProperty(Properties.FACING)) continue;
  1384.             if (world.getBlockState(pos.offset(d)).getValue(Properties.FACING) != d) continue;
  1385.             BlockState state = world.getBlockState(pos.offset(d));
  1386.             funcs.add((obj) -> { world.setBlockState(pos.offset(d.rotateYClockwise()), state.rotate(BlockRotation.CLOCKWISE_90)); });
  1387.         }
  1388.         for (Consumer<Object> c : funcs) c.apply(null);
  1389.     }
  1390.    
  1391.     private boolean isPowered (World world, BlockPos pos) {
  1392.         for (Direction d : Direction.stream().collect(Collectors.asList()))
  1393.             if (d.getAxis() != Direction.Axis.Y && world.getStrongRedtonePower(pos, d) > 0) return true;
  1394.         return false;
  1395.     }
  1396.    
  1397.     protected ActionResult onUse (BlockState state, World world, BlockPos pos, PlayerEntity player, BlockHitResult hit) {
  1398.         if (!state.hasProperty(TURNTABLE_TIMER) || !state.hasProperty(TURNTABLE_SPEED)) return ActionResult.PASS;
  1399.         if (hit.getSide().getAxis() == Direction.Axis.Y) return ActionResult.PASS;
  1400.         state.with(TURNTABLE_TIMER, Integer.valueOf(0));
  1401.         state.cycle(TURNTABLE_SPEED);
  1402.     }
  1403. }
  1404.  
  1405. public class ScrewPumpBlock extends HorizontalFacingBlock implements MechanicalPowerBlock {
  1406.     public ScrewPumpBlock (AbstractBlock.Settings settings) {
  1407.         super (settings);
  1408.         this.setDefaultState(this.getStateManager().getDefaultState().with(HorizontalFacingBlock.FACING, Direction.DOWN));
  1409.     }
  1410.    
  1411.     public BlockState getPlacementState (ItemPlacementContext ctx) {
  1412.         return this.getDefaultState().withIfExists(HorizontalFacingBlock.FACING, ctx.getSide());
  1413.     }
  1414.    
  1415.     public boolean recievesPower (World world, BlockPos pos, Direction toDir) {
  1416.         return toDir == Direction.DOWN;
  1417.     }
  1418.    
  1419.     public boolean isSimple () {
  1420.         return false;
  1421.     }
  1422.    
  1423.     public void rotationTick (BlockState state, World world, BlockPos pos) {
  1424.         if (!world.getBlockState(pos.up()).isReplaceable()) return;
  1425.         if (!state.hasProperty(HorizontalFacingBlock.FACING)) return;
  1426.         if (this.badFlow(world, pos, state.getValue(HorizontalFacingBlock.FACING))) return;
  1427.         world.setBlockState(pos.up(), Blocks.WATER.getDefaultState().with(FluidBlock.LEVEL, Integer.valueOf(((int) world.getFluidState(pos.offset(state.getValue(HorizontalFacingBlock.FACING))).getLevel()) - 2)));
  1428.     }
  1429.    
  1430.     private boolean badFlow (World world, BlockPos pos, Direction to) {
  1431.         Vec3 vel = world.getFluidState(pos.offset(to)).getVelocity(world, pos.offset(to));
  1432.         double x = vel.getX();
  1433.         double y = vel.getY();
  1434.         double z = vel.getZ();
  1435.         double greatestH = Math.abs(x) >= Math.abs(z) ? x : z;
  1436.         double greatest = Math.abs(greatestH) >= Math.abs(y) ? greatestH : y;
  1437.         if (Math.abs(y) >= Math.abs(x) || Math.abs(y) >= Math.abs(z)) return true;
  1438.         if (Direction.getAxis() == Direction.Axis.X && Math.abs(z) >= Math.abs(x)) return true;
  1439.         if (Direction.getAxis() == Direction.Axis.Z && Math.abs(x) >= Math.abs(z)) return true;
  1440.         if (Direction.getAxisDirection() == Direction.AxisDirection.NEGATIVE && Math.abs(greatest) == greatest) return true;
  1441.         if (Direction.getAxisDirection() == Direction.AxisDirection.POSITIVE && Math.abs(greatest) != greatest) return true;
  1442.         return false;
  1443.     }
  1444. }
  1445.  
  1446. public class CrankshaftBlock extends Block implements MechanicalPowerBlock {
  1447.     public CrankshaftBlock (AbstractBlock.Settings settings) {
  1448.         super(settings);
  1449.     }
  1450.    
  1451.     public boolean recievesPower (World world, BlockPos pos, Direction toDir) {
  1452.         return toDir == Direction.UP && world.getBlockState(pos).hasProperty(AXIS) && world.getBlockState(pos.up()).hasProperty(HandCrankBlock.AXIS) && world.getBlockState(pos).getValue(AXIS) == world.getBlockState(pos.up()).getValue(HandCrankBlock.AXIS);
  1453.     }
  1454.    
  1455.     public boolean isSimple () {
  1456.         return true;
  1457.     }
  1458.    
  1459.     public void rotationTick (BlockState state, World world, BlockPos pos) {
  1460.         if (!state.hasProperty(AXIS)) return;
  1461.         for (Direction.AxisDirection d : List.<Direction.AxisDirection>of(Direction.AxisDirection.POSITIVE, Direction.AxisDirection.NEGATIVE))
  1462.             if (world.getBlockState(pos.offset(Direction.from(state.getValue(AXIS), d))).getBlock() instanceof AxleBlock b && b.recievesPower(world, pos.offset(Direction.from(state.getValue(AXIS), d)), Direction.from(state.getValue(AXIS), d).getOpposite()))
  1463.                 b.rotationTick(world.getBlockState(pos.offset(Direction.from(state.getValue(AXIS), d))), world, pos.offset(Direction.from(state.getValue(AXIS), d)));
  1464.     }
  1465. }
  1466.  
  1467. public class HandCrankBlock extends BlockWithEntity {
  1468.     public static final EnumProperty<Direction.Axis> AXIS = Properties.HORIZONTAL_AXIS;
  1469.    
  1470.     public HandCrankBlock (AbstractBlock.Settings settings) {
  1471.         super (settings);
  1472.     }
  1473.    
  1474.     public MapCodec<HandCrankBlock> getCodec () {
  1475.         return AbstractBlock.simpleCodec(HandCrankBlock::new);
  1476.     }
  1477.    
  1478.     public HandCrankBlockEntity newBlockEntity (BlockPos pos, BlockState state) {
  1479.         return new HandCrankBlockEntity(pos, state);
  1480.     }
  1481.    
  1482.     public HandCrankBlockEntity getOrCreateBlockEntity (World world, BlockPos pos) {
  1483.         if (world.getBlockEntity(pos) instanceof HandCrankBlockEntity hc) {
  1484.             return hc;
  1485.         } else {
  1486.             HandCrankBlockEntity e = new HandCrankBlockEntity(pos, world.getBlockState(pos));
  1487.             e.setWorld(world);
  1488.             return e;
  1489.         }
  1490.     }
  1491.    
  1492.     public ActionResult onUse (BlockState state, World world, BlockPos pos, PlayerEntity player, BlockHitResult hit) {
  1493.         if (this.isActive(world, pos)) return ActionResult.FAILURE;
  1494.         this.setActive(world, pos);
  1495.     }
  1496.    
  1497.     protected VoxelShape getRaycastShape (BlockState state, BlockView world, BlockPos pos) {
  1498.         return this.getShape(state.hasProperty(DIRECTION) ? state.getValue(DIRECTION) : Direction.UP);
  1499.     }
  1500.    
  1501.     protected VoxelShape getOutlineShape (BlockState state, BlockView world, BlockPos pos, ShapeContext context) {
  1502.         return this.getShape(state.hasProperty(DIRECTION) ? state.getValue(DIRECTION) : Direction.UP);
  1503.     }
  1504.    
  1505.     protected VoxelShape getCollisionShape (BlockState state, BlockView world, BlockPos pos, ShapeContext context) {
  1506.         return this.getShape(state.hasProperty(DIRECTION) ? state.getValue(DIRECTION) : Direction.UP);
  1507.     }
  1508.    
  1509.     private VoxelShape getShape (Direction.Axis axis) {
  1510.         if (axis == Direction.Axis.X)
  1511.             return VoxelShapes.union(VoxelShapes.cuboid(0.0, 0.0, 0.0, 16.0, 4.0, 16.0), VoxelShapes.cuboid(6.0, 4.0, 0.0, 10.0, 14.0, 16.0));
  1512.         if (axis == Direction.Axis.Z)
  1513.             return VoxelShapes.union(VoxelShapes.cuboid(0.0, 0.0, 0.0, 16.0, 4.0, 16.0), VoxelShapes.cuboid(0.0, 4.0, 6.0, 0.0, 14.0, 10.0));
  1514.         return VoxelShapes.fullCube();
  1515.     }
  1516.    
  1517.     public boolean suppliesPower () {
  1518.         return true;
  1519.     }
  1520.    
  1521.     private boolean isActive (World world, BlockPos pos) {
  1522.         return world.getBlockState(pos).hasProperty(Properties.ACTIVE) && world.getBlockState(pos).getValue(Properties.ACTIVE).booleanValue();
  1523.     }
  1524.    
  1525.     private boolean setActive (World world, BlockPos pos) {
  1526.         return world.setBlockState(pos, world.getBlockState(pos).withIfExists(Properties.ACTIVE, Boolean.TRUE));
  1527.     }
  1528.    
  1529.     public BlockEntityTicker<HandCrankBlockEntity> getTicker (World world, BlockState state, BlockEntityType<HandCrankBlockEntity> type) {
  1530.         return (w, p, s, t) -> { if (this.isActive(w, p)) t.activeTick(); };
  1531.     }
  1532. }
  1533.  
  1534. public class HandCrankBlockEntity extends BlockEntity implements MechanicalPowerSupplier {
  1535.     private int timer = 0;
  1536.    
  1537.     public HandCrankBlockEntity (BlockPos pos, BlockState state) {
  1538.         super (BTWBlockEntityTypes.HAND_CRANK, pos, state);
  1539.     }
  1540.    
  1541.     public void activeTick () {
  1542.         this.timer++;
  1543.         if (this.timer >= 40) {
  1544.             this.timer = 0;
  1545.             this.getWorld().setBlockState(this.getBlockPos(), this.getWorld().getBlockState(this.getBlockPos()).withIfExists(Properties.ACTIVE, Boolean.FALSE));
  1546.             this.dispatchRotationStopped();
  1547.         } else {
  1548.             this.validatePos();
  1549.             this.dispatchRotationTick();
  1550.         }
  1551.     }
  1552.    
  1553.     public void dispatchRotationTick () {
  1554.         for (Direction d : Direction.stream.collect(Collectors.asList())) {
  1555.             if (d == Direction.UP) continue;
  1556.             if (this.getWorld().getBlockState(this.getBlockPos()).getBlock() instanceof MechanicalPowerBlock b && b.isSimple())
  1557.                 b.rotationTick();
  1558.         }
  1559.     }
  1560.    
  1561.     public void validatePos () {
  1562.         boolean found;
  1563.         for (Direction d : Direction.stream.collect(Collectors.asList())) {
  1564.             if (d == Direction.UP) continue;
  1565.             if (this.getWorld().getBlockState(this.getBlockPos()).getBlock() instanceof MechanicalPowerBlock b && b.isSimple()) {
  1566.                 if (found) this.getWorld().destroyBlock(this.getBlockPos(), false);
  1567.                 found = true;
  1568.             }
  1569.         }
  1570.     }
  1571.    
  1572.     public void dispatchRotationStopped () {
  1573.         for (Direction d : Direction.stream.collect(Collectors.asList())) {
  1574.             if (d == Direction.UP) continue;
  1575.             if (this.getWorld().getBlockState(this.getBlockPos()).getBlock() instanceof MechanicalPowerBlock b && b.isSimple())
  1576.                 b.rotationStopped();
  1577.         }
  1578.     }
  1579. }
  1580.  
  1581. public class FCHopperScreenHandler extends ScreenHandler {
  1582.     private final Inventory container;
  1583.     private final PlayerInventory inventory;
  1584.    
  1585.     public FCHopperScreenHandler (MenuType<?> type, int syncId, PlayerInventory playerInventory, Inventory container) {
  1586.         super(type, syncId);
  1587.         checkContainerSize(container, 19);
  1588.         this.container = container;
  1589.         container.startOpen(playerInventory.player);
  1590.        
  1591.         this.addSlot(new Slot(container, 0, 87, 17));
  1592.         for (int i = 0; i < 18; i++) {
  1593.             this.addSlot(new Slot(container, i + 1, 15 + ((i % 9) * 18), 41 + (i * 2)));
  1594.         }
  1595.        
  1596.         for (int i = 0; i < 27; i++) {
  1597.             this.addSlot(new Slot(playerInventory, i + 9, 15 + ((i % 9) * 18), 93 + (i * 2)));
  1598.         }
  1599.        
  1600.         for (int i = 0; i < 9; i++) {
  1601.             this.addSlot(new Slot(playerInventory, i, 15 + ((i % 9) * 18), 153 + (i * 2)));
  1602.         }
  1603.     }
  1604.    
  1605.     public boolean canUse (PlayerEntity player) {
  1606.         return this.container.canUse(player);
  1607.     }
  1608.    
  1609.     public ItemStack quickMoveStack (PlayerEntity player, int index) {
  1610.         ItemStack stack = ItemStack.EMPTY;
  1611.         Slot slot = this.slots.get(index);
  1612.         if (slot != null && slot.hasItem()) {
  1613.             if (index < 19) {
  1614.                 if (!this.insertItem(slot.getItem(), 19, this.slots.size(), true)) {
  1615.                     return ItemStack.EMPTY;
  1616.                 }
  1617.             } else if (!this.insertItem(slot.getItem(), 0, 19, false)) {
  1618.                 return ItemStack.EMPTY;
  1619.             }
  1620.             if (stack.isEmpty()) {
  1621.                 slot.setByPlayer(ItemStack.EMPTY);
  1622.             } else {
  1623.                 slot.setChanged();
  1624.             }
  1625.         }
  1626.        
  1627.         return slot.getItem().copy();
  1628.     }
  1629.    
  1630.     public void removed (PlayerEntity player) {
  1631.         super.removed(player);
  1632.         this.container.stopOpen(player);
  1633.     }
  1634.    
  1635.     public Inventory getInventory () {
  1636.         return this.container;
  1637.     }
  1638.    
  1639.     public PlayerInventory getPlayerInventory () {
  1640.         return this.inventory;
  1641.     }
  1642. }
  1643.  
  1644. public class FCHopperScreen extends HandledScreen<FCHopperScreenHandler> {
  1645.     public static final Identifier BACKGROUND_TEXTURE = Identifier.of("better_than_wolves", "textures/gui/fc_hopper");
  1646.    
  1647.     public FCHopperScreen (FCHopperScreenHandler menu) {
  1648.         super (menu, menu.getPlayerInventory(), menu.getInventory());
  1649.     }
  1650.    
  1651.     protected void drawBackground (DrawContext context, float delta, int mouseX, int mouseY) {
  1652.         int distLeft = (this.width - this.imageWidth) / 2;
  1653.         int distTop = (this.height - this.imageHeight) / 2;
  1654.         context.drawTexture(BACKGROUND_TEXTURE, distLeft, distTop, 0, 0, this.imageWidth, 96);
  1655.     }
  1656. }
  1657.  
  1658. public class SmelterContainerScreenHandler extends ScreenHandler {
  1659.     private final Inventory container;
  1660.     private final PlayerInventory inventory;
  1661.    
  1662.     public SmelterContainerScreenHandler (MenuType<?> type, int syncId, PlayerInventory playerInventory, Inventory container) {
  1663.         super(type, syncId);
  1664.         checkContainerSize(container, 27);
  1665.         this.container = container;
  1666.         container.startOpen(playerInventory.player);
  1667.        
  1668.         for (int i = 0; i < 27; i++) {
  1669.             this.addSlot(new Slot(container, i + 1, 15 + ((i % 9) * 18), 41 + (i * 2)));
  1670.         }
  1671.        
  1672.         for (int i = 0; i < 27; i++) {
  1673.             this.addSlot(new Slot(playerInventory, i + 9, 15 + ((i % 9) * 18), 93 + (i * 2)));
  1674.         }
  1675.        
  1676.         for (int i = 0; i < 9; i++) {
  1677.             this.addSlot(new Slot(playerInventory, i, 15 + ((i % 9) * 18), 153 + (i * 2)));
  1678.         }
  1679.     }
  1680.    
  1681.     public boolean canUse (PlayerEntity player) {
  1682.         return this.container.canUse(player);
  1683.     }
  1684.    
  1685.     public ItemStack quickMoveStack (PlayerEntity player, int index) {
  1686.         ItemStack stack = ItemStack.EMPTY;
  1687.         Slot slot = this.slots.get(index);
  1688.         if (slot != null && slot.hasItem()) {
  1689.             if (index < 27) {
  1690.                 if (!this.insertItem(slot.getItem(), 27, this.slots.size(), true)) {
  1691.                     return ItemStack.EMPTY;
  1692.                 }
  1693.             } else if (!this.insertItem(slot.getItem(), 0, 27, false)) {
  1694.                 return ItemStack.EMPTY;
  1695.             }
  1696.             if (stack.isEmpty()) {
  1697.                 slot.setByPlayer(ItemStack.EMPTY);
  1698.             } else {
  1699.                 slot.setChanged();
  1700.             }
  1701.         }
  1702.        
  1703.         return slot.getItem().copy();
  1704.     }
  1705.    
  1706.     public void removed (PlayerEntity player) {
  1707.         super.removed(player);
  1708.         this.container.stopOpen(player);
  1709.     }
  1710.    
  1711.     public Inventory getInventory () {
  1712.         return this.container;
  1713.     }
  1714.    
  1715.     public PlayerInventory getPlayerInventory () {
  1716.         return this.inventory;
  1717.     }
  1718. }
  1719.  
  1720. public class SmelterContainerScreen extends HandledScreen<SmelterContainerScreenHandler> {
  1721.     public static final Identifier BACKGROUND_TEXTURE = Identifier.of("better_than_wolves", "textures/gui/fc_cauldron");
  1722.     public static final Identifier BACKGROUND_TEXTURE = Identifier.of("better_than_wolves", "textures/gui/fc_cauldron_fire");
  1723.    
  1724.     public SmelterContainerScreen (SmelterContainerScreenHandler menu) {
  1725.         super (menu, menu.getPlayerInventory(), menu.getInventory());
  1726.     }
  1727.    
  1728.     protected void drawBackground (DrawContext context, float delta, int mouseX, int mouseY) {
  1729.         int distLeft = (this.width - this.imageWidth) / 2;
  1730.         int distTop = (this.height - this.imageHeight) / 2;
  1731.         context.drawTexture(BACKGROUND_TEXTURE, distLeft, distTop, 0, 0, this.imageWidth, 96);
  1732.         if (this.menu.isActive()) {
  1733.             int progress = Mth.ceil(this.menu.getProgress() * 13.0F) + 1;
  1734.             context.drawGuiTexture(this.litProgressSprite, 14, 14, 0, 14 - progress, this.leftPos + 89, this.topPos + 12 + 14 - progress, 14, progress);
  1735.         }
  1736.     }
  1737. }
Add Comment
Please, Sign In to add comment