diff --git a/README.md b/README.md index 1fdf2b1..7f0f146 100644 --- a/README.md +++ b/README.md @@ -22,11 +22,8 @@ or based on preferences add the person(s) to the project ### What Shapes are planed to be added Currently, the list of shapes to be added is pretty simple as the mod is still under development: - Fence -- Pane - Button - Pressure Plate -- Trapdoor -- Door - Carpet (maybe redundant with Layer) - Post - Half Slab (maybe redundant with Layer) diff --git a/src/main/java/fr/adrien1106/reframed/ReFramed.java b/src/main/java/fr/adrien1106/reframed/ReFramed.java index d9cb3e3..3e9b5f9 100644 --- a/src/main/java/fr/adrien1106/reframed/ReFramed.java +++ b/src/main/java/fr/adrien1106/reframed/ReFramed.java @@ -37,7 +37,7 @@ public class ReFramed implements ModInitializer { public static final String MODID = "reframed"; public static final ArrayList BLOCKS = new ArrayList<>(); - public static Block CUBE, SMALL_CUBE, SMALL_CUBES_STEP, STAIR, HALF_STAIR, STAIRS_CUBE, HALF_STAIRS_SLAB, HALF_STAIRS_STAIR, SLAB, SLABS_CUBE, STEP, STEPS_SLAB, LAYER, PILLAR, PILLARS_WALL, WALL; + public static Block CUBE, SMALL_CUBE, SMALL_CUBES_STEP, STAIR, HALF_STAIR, STAIRS_CUBE, HALF_STAIRS_SLAB, HALF_STAIRS_STAIR, SLAB, SLABS_CUBE, STEP, STEPS_SLAB, LAYER, PILLAR, PILLARS_WALL, WALL, PANE, TRAPDOOR, DOOR; public static final ArrayList ITEMS = new ArrayList<>(); public static Item HAMMER, SCREWDRIVER, BLUEPRINT, BLUEPRINT_WRITTEN; @@ -67,6 +67,9 @@ public class ReFramed implements ModInitializer { PILLAR = registerBlock("pillar" , new ReFramedPillarBlock(cp(Blocks.OAK_FENCE))); PILLARS_WALL = registerBlock("pillars_wall" , new ReFramedPillarsWallBlock(cp(Blocks.OAK_FENCE))); WALL = registerBlock("wall" , new ReFramedWallBlock(cp(Blocks.OAK_FENCE))); + PANE = registerBlock("pane" , new ReFramedPaneBlock(cp(Blocks.OAK_FENCE))); + TRAPDOOR = registerBlock("trapdoor" , new ReFramedTrapdoorBlock(cp(Blocks.OAK_TRAPDOOR))); + DOOR = registerBlock("door" , new ReFramedDoorBlock(cp(Blocks.OAK_DOOR))); HAMMER = registerItem("hammer" , new ReFramedHammerItem(new Item.Settings().maxCount(1))); SCREWDRIVER = registerItem("screwdriver" , new ReFramedScrewdriverItem(new Item.Settings().maxCount(1))); diff --git a/src/main/java/fr/adrien1106/reframed/block/ReFramedBlock.java b/src/main/java/fr/adrien1106/reframed/block/ReFramedBlock.java index cc00757..0db4a18 100644 --- a/src/main/java/fr/adrien1106/reframed/block/ReFramedBlock.java +++ b/src/main/java/fr/adrien1106/reframed/block/ReFramedBlock.java @@ -42,9 +42,7 @@ public class ReFramedBlock extends Block implements BlockEntityProvider { super(settings); setDefaultState(getDefaultState().with(LIGHT, false)); } - - //For addon devs: override this so your blocks don't end up trying to place my block entity, my BlockEntityType only handles blocks internal to the mod - //Just make your own BlockEntityType, it's fine, you can even use the same ReFramedEntity class + @Override public @Nullable BlockEntity createBlockEntity(BlockPos pos, BlockState state) { return ReFramed.REFRAMED_BLOCK_ENTITY.instantiate(pos, state); @@ -63,18 +61,13 @@ public class ReFramedBlock extends Block implements BlockEntityProvider { @Override public ActionResult onUse(BlockState state, World world, BlockPos pos, PlayerEntity player, Hand hand, BlockHitResult hit) { - if (!canUse(world, pos, player)) return superUse(state, world, pos, player, hand, hit); + if (!canUse(world, pos, player)) return ActionResult.PASS; ActionResult result = BlockHelper.useUpgrade(state, world, pos, player, hand); if (result.isAccepted()) return result; return BlockHelper.useCamo(state, world, pos, player, hand, hit, 1); } - // don't like this but might be useful - protected ActionResult superUse(BlockState state, World world, BlockPos pos, PlayerEntity player, Hand hand, BlockHitResult hit) { - return super.onUse(state, world, pos, player, hand, hit); - } - protected boolean canUse(World world, BlockPos pos, PlayerEntity player) { return player.canModifyBlocks() && world.canPlayerModifyAt(player, pos); } diff --git a/src/main/java/fr/adrien1106/reframed/block/ReFramedDoorBlock.java b/src/main/java/fr/adrien1106/reframed/block/ReFramedDoorBlock.java new file mode 100644 index 0000000..3fd988e --- /dev/null +++ b/src/main/java/fr/adrien1106/reframed/block/ReFramedDoorBlock.java @@ -0,0 +1,273 @@ +package fr.adrien1106.reframed.block; + +import fr.adrien1106.reframed.util.VoxelHelper; +import fr.adrien1106.reframed.util.blocks.BlockHelper; +import net.minecraft.block.*; +import net.minecraft.block.entity.BlockEntity; +import net.minecraft.block.enums.DoorHinge; +import net.minecraft.block.enums.DoubleBlockHalf; +import net.minecraft.entity.LivingEntity; +import net.minecraft.entity.ai.pathing.NavigationType; +import net.minecraft.entity.player.PlayerEntity; +import net.minecraft.fluid.Fluids; +import net.minecraft.item.ItemPlacementContext; +import net.minecraft.item.ItemStack; +import net.minecraft.sound.SoundCategory; +import net.minecraft.state.StateManager; +import net.minecraft.util.ActionResult; +import net.minecraft.util.BlockMirror; +import net.minecraft.util.BlockRotation; +import net.minecraft.util.Hand; +import net.minecraft.util.hit.BlockHitResult; +import net.minecraft.util.math.BlockPos; +import net.minecraft.util.math.Direction; +import net.minecraft.util.math.MathHelper; +import net.minecraft.util.math.Vec3d; +import net.minecraft.util.shape.VoxelShape; +import net.minecraft.world.BlockView; +import net.minecraft.world.World; +import net.minecraft.world.WorldAccess; +import net.minecraft.world.WorldView; +import net.minecraft.world.event.GameEvent; +import net.minecraft.world.explosion.Explosion; +import org.jetbrains.annotations.Nullable; + +import java.util.function.BiConsumer; + +import static net.minecraft.state.property.Properties.*; + +public class ReFramedDoorBlock extends WaterloggableReFramedBlock { + + public static final VoxelShape[] DOOR_VOXELS; + + public ReFramedDoorBlock(Settings settings) { + super(settings); + setDefaultState(getDefaultState() + .with(HORIZONTAL_FACING, Direction.NORTH) + .with(DOOR_HINGE, DoorHinge.LEFT) + .with(DOUBLE_BLOCK_HALF, DoubleBlockHalf.LOWER) + .with(OPEN, false) + .with(POWERED, false) + ); + } + + @Override + protected void appendProperties(StateManager.Builder builder) { + super.appendProperties(builder.add(HORIZONTAL_FACING, DOOR_HINGE, DOUBLE_BLOCK_HALF, OPEN, POWERED)); + } + + @Override + public boolean canPlaceAt(BlockState state, WorldView world, BlockPos pos) { + BlockPos pos_down = pos.down(); + BlockState state_down = world.getBlockState(pos_down); + return state.get(DOUBLE_BLOCK_HALF) == DoubleBlockHalf.LOWER ? !state_down.isAir() : state_down.isOf(this); + } + + @Override + public void neighborUpdate(BlockState state, World world, BlockPos pos, Block source, BlockPos sourcePos, boolean notify) { + if (world.isClient) return; + boolean powered = world.isReceivingRedstonePower(pos) + || world.isReceivingRedstonePower( + state.get(DOUBLE_BLOCK_HALF) == DoubleBlockHalf.LOWER + ? pos.up() + : pos.down() + ); + if (!getDefaultState().isOf(source) && powered != state.get(POWERED)) { + if (state.get(OPEN) != powered) + playToggleSound(null, world, pos, powered); + + world.setBlockState(pos, state.with(POWERED, powered).with(OPEN, powered), 2); + if (state.get(WATERLOGGED)) { + world.scheduleFluidTick(pos, Fluids.WATER, Fluids.WATER.getTickRate(world)); + } + } + } + + @Override + public @Nullable BlockState getPlacementState(ItemPlacementContext ctx) { + World world = ctx.getWorld(); + BlockPos pos = ctx.getBlockPos(); + Direction facing = ctx.getHorizontalPlayerFacing().getOpposite(); + if (pos.getY() >= world.getTopY() - 1 || !world.getBlockState(pos.up()).canReplace(ctx)) return null; + BlockState state = super.getPlacementState(ctx) + .with(DOUBLE_BLOCK_HALF, DoubleBlockHalf.LOWER) + .with(HORIZONTAL_FACING, facing); + + if (world.isReceivingRedstonePower(pos) || world.isReceivingRedstonePower(pos.up())) + state = state.with(OPEN, true).with(POWERED, true); + + + return state.with(DOOR_HINGE, getHinge(facing, pos, world, BlockHelper.getRelativePos(ctx.getHitPos(), pos))); + } + + @Override + public void onPlaced(World world, BlockPos pos, BlockState state, @Nullable LivingEntity placer, ItemStack stack, BlockState old_state, BlockEntity old_entity) { + world.setBlockState( + pos.up(), + state + .with(DOUBLE_BLOCK_HALF, DoubleBlockHalf.UPPER) + .with(WATERLOGGED, world.getFluidState(pos.up()).isOf(Fluids.WATER)), + 3 + ); + } + + @Override + public BlockState onBreak(World world, BlockPos pos, BlockState state, PlayerEntity player) { + if (!world.isClient() && (player.isCreative() || !player.canHarvest(state))) { + DoubleBlockHalf half = state.get(DOUBLE_BLOCK_HALF); + BlockPos other_pos = half == DoubleBlockHalf.LOWER ? pos.up() : pos.down(); + BlockState other_state = world.getBlockState(other_pos); + if (other_state.isOf(this) && other_state.get(DOUBLE_BLOCK_HALF) != half) { + world.setBlockState(other_pos, other_state.get(WATERLOGGED) ? Blocks.WATER.getDefaultState(): Blocks.AIR.getDefaultState(), 35); + world.syncWorldEvent(player, 2001, other_pos, Block.getRawIdFromState(other_state)); + } + } + + return super.onBreak(world, pos, state, player); + } + + @Override + public void onStateReplaced(BlockState state, World world, BlockPos pos, BlockState newState, boolean moved) { + super.onStateReplaced(state, world, pos, newState, moved); + + if(!state.isOf(newState.getBlock())) world.removeBlockEntity(pos); + } + + @Override + public BlockState getStateForNeighborUpdate(BlockState state, Direction direction, BlockState other, WorldAccess world, BlockPos pos, BlockPos moved) { + if (direction.getAxis() == Direction.Axis.Y + && other.isOf(this) + && other.get(DOUBLE_BLOCK_HALF) != state.get(DOUBLE_BLOCK_HALF) + && other.get(OPEN) != state.get(OPEN) + ) return state.cycle(OPEN); + Direction facing = state.get(HORIZONTAL_FACING); + if (direction == ( + state.get(DOOR_HINGE) == DoorHinge.RIGHT + ? facing.rotateYClockwise() + : facing.rotateYCounterclockwise()) + && other.isOf(this) + && other.get(DOUBLE_BLOCK_HALF) == state.get(DOUBLE_BLOCK_HALF) + && other.get(DOOR_HINGE) != state.get(DOOR_HINGE) + && !state.get(POWERED) + ) return state.with(OPEN, other.get(OPEN)); + return super.getStateForNeighborUpdate(state, direction, other, world, pos, moved); + } + + @Override + public ActionResult onUse(BlockState state, World world, BlockPos pos, PlayerEntity player, Hand hand, BlockHitResult hit) { + ActionResult result = super.onUse(state, world, pos, player, hand, hit); + if (result.isAccepted()) return result; + flip(state, world, pos, player); + return ActionResult.success(world.isClient); + } + + @Override + public boolean canPathfindThrough(BlockState state, BlockView world, BlockPos pos, NavigationType type) { + return switch (type) { + case LAND, AIR -> state.get(OPEN); + case WATER -> false; + }; + } + + @Override + public void onExploded(BlockState state, World world, BlockPos pos, Explosion explosion, BiConsumer stack_merger) { + if (explosion.getDestructionType() == Explosion.DestructionType.TRIGGER_BLOCK + && !world.isClient() + && !state.get(POWERED) + ) flip(state, world, pos, null); + + super.onExploded(state, world, pos, explosion, stack_merger); + } + + private void flip(BlockState state, World world, BlockPos pos, @Nullable PlayerEntity player) { + state = state.cycle(OPEN); + world.setBlockState(pos, state, 10); + + this.playToggleSound(player, world, pos, state.get(OPEN)); + } + + protected void playToggleSound(@Nullable PlayerEntity player, World world, BlockPos pos, boolean open) { + world.playSound(player, pos, open ? BlockSetType.OAK.doorOpen() : BlockSetType.OAK.doorClose(), SoundCategory.BLOCKS, 1.0F, world.getRandom().nextFloat() * 0.1F + 0.9F); + world.emitGameEvent(player, open ? GameEvent.BLOCK_OPEN : GameEvent.BLOCK_CLOSE, pos); + } + + @Override + public VoxelShape getOutlineShape(BlockState state, BlockView world, BlockPos pos, ShapeContext context) { + Direction direction = state.get(HORIZONTAL_FACING); + if (state.get(OPEN)) direction = switch (state.get(DOOR_HINGE)) { + case RIGHT -> direction.rotateYCounterclockwise(); + case LEFT -> direction.rotateYClockwise(); + }; + return DOOR_VOXELS[direction.ordinal() - 2]; + } + + @Override + public BlockState rotate(BlockState state, BlockRotation rotation) { + return state.with(HORIZONTAL_FACING, rotation.rotate(state.get(HORIZONTAL_FACING))); + } + + @Override + public BlockState mirror(BlockState state, BlockMirror mirror) { + return mirror == BlockMirror.NONE ? state : state.with(HORIZONTAL_FACING, mirror.apply(state.get(HORIZONTAL_FACING))).cycle(DOOR_HINGE); + } + + @Override + public long getRenderingSeed(BlockState state, BlockPos pos) { + return MathHelper.hashCode(pos.getX(), pos.down(state.get(DOUBLE_BLOCK_HALF) == DoubleBlockHalf.LOWER ? 0 : 1).getY(), pos.getZ()); + } + + private DoorHinge getHinge(Direction facing, BlockPos pos, World world, Vec3d hit_pos) { + Direction left = facing.rotateYClockwise(); + BlockPos left_pos = pos.offset(left); + BlockState left_state = world.getBlockState(left_pos); + Direction right = facing.rotateYCounterclockwise(); + BlockPos right_pos = pos.offset(right); + BlockState right_state = world.getBlockState(right_pos); + DoorHinge hinge = null; + + if (left_state.isSideSolidFullSquare(world, left_pos, right)) + hinge = DoorHinge.LEFT; + if (right_state.isSideSolidFullSquare(world, right_pos, left)) + hinge = hinge == DoorHinge.LEFT ? null : DoorHinge.RIGHT; + + if (hinge != null) return hinge; + + if (left_state.isOf(this) + && left_state.get(HORIZONTAL_FACING) == facing + && left_state.get(DOUBLE_BLOCK_HALF) == DoubleBlockHalf.LOWER + && left_state.get(DOOR_HINGE) == DoorHinge.LEFT + ) hinge = DoorHinge.RIGHT; + if (right_state.isOf(this) + && right_state.get(HORIZONTAL_FACING) == facing + && right_state.get(DOUBLE_BLOCK_HALF) == DoubleBlockHalf.LOWER + && right_state.get(DOOR_HINGE) == DoorHinge.RIGHT + ) hinge = hinge == DoorHinge.RIGHT ? null : DoorHinge.LEFT; + + if (hinge != null) return hinge; + + return switch (facing.getAxis()) { + case Z -> { + if (left.getDirection() == Direction.AxisDirection.POSITIVE) + yield hit_pos.getX() < 0.5 ? DoorHinge.RIGHT : DoorHinge.LEFT; + else // left.getDirection() == Direction.AxisDirection.NEGATIVE + yield hit_pos.getX() < 0.5 ? DoorHinge.LEFT : DoorHinge.RIGHT; + } + case X -> { + if (left.getDirection() == Direction.AxisDirection.POSITIVE) + yield hit_pos.getZ() < 0.5 ? DoorHinge.RIGHT : DoorHinge.LEFT; + else // left.getDirection() == Direction.AxisDirection.NEGATIVE + yield hit_pos.getZ() < 0.5 ? DoorHinge.LEFT : DoorHinge.RIGHT; + } + default -> DoorHinge.LEFT; + }; + } + + static { + VoxelShape SHAPE = createCuboidShape(0.0, 0.0, 0.0, 16.0, 16.0, 3.0); + DOOR_VOXELS = VoxelHelper.VoxelListBuilder.create(SHAPE, 4) + .add(VoxelHelper::mirrorZ) + .add(VoxelHelper::rotateY) + .add(VoxelHelper::mirrorX) + .build(); + } +} diff --git a/src/main/java/fr/adrien1106/reframed/block/ReFramedDoubleBlock.java b/src/main/java/fr/adrien1106/reframed/block/ReFramedDoubleBlock.java index d29f2e2..7f67064 100644 --- a/src/main/java/fr/adrien1106/reframed/block/ReFramedDoubleBlock.java +++ b/src/main/java/fr/adrien1106/reframed/block/ReFramedDoubleBlock.java @@ -79,7 +79,7 @@ public abstract class ReFramedDoubleBlock extends ReFramedBlock { @Override public ActionResult onUse(BlockState state, World world, BlockPos pos, PlayerEntity player, Hand hand, BlockHitResult hit) { - if (!canUse(world, pos, player)) return superUse(state, world, pos, player, hand, hit); + if (!canUse(world, pos, player)) return ActionResult.PASS; ActionResult result = BlockHelper.useUpgrade(state, world, pos, player, hand); if (result.isAccepted()) return result; return BlockHelper.useCamo(state, world, pos, player, hand, hit, getHitShape(state, hit)); diff --git a/src/main/java/fr/adrien1106/reframed/block/ReFramedPaneBlock.java b/src/main/java/fr/adrien1106/reframed/block/ReFramedPaneBlock.java new file mode 100644 index 0000000..a800d1c --- /dev/null +++ b/src/main/java/fr/adrien1106/reframed/block/ReFramedPaneBlock.java @@ -0,0 +1,130 @@ +package fr.adrien1106.reframed.block; + +import fr.adrien1106.reframed.util.VoxelHelper; +import net.minecraft.block.Block; +import net.minecraft.block.BlockState; +import net.minecraft.block.PaneBlock; +import net.minecraft.block.ShapeContext; +import net.minecraft.item.ItemPlacementContext; +import net.minecraft.registry.tag.BlockTags; +import net.minecraft.state.StateManager; +import net.minecraft.state.property.BooleanProperty; +import net.minecraft.util.BlockMirror; +import net.minecraft.util.BlockRotation; +import net.minecraft.util.math.BlockPos; +import net.minecraft.util.math.Direction; +import net.minecraft.util.shape.VoxelShape; +import net.minecraft.util.shape.VoxelShapes; +import net.minecraft.world.BlockView; +import net.minecraft.world.World; +import net.minecraft.world.WorldAccess; +import org.jetbrains.annotations.Nullable; + +import static net.minecraft.state.property.Properties.*; + +public class ReFramedPaneBlock extends WaterloggableReFramedBlock { + + public static final VoxelShape[] PANE_VOXELS; + + public ReFramedPaneBlock(Settings settings) { + super(settings); + setDefaultState(getDefaultState() + .with(EAST, false) + .with(NORTH, false) + .with(WEST, false) + .with(SOUTH, false) + ); + } + + @Override + protected void appendProperties(StateManager.Builder builder) { + super.appendProperties(builder.add(EAST, NORTH, SOUTH, WEST)); + } + + @Override + public BlockState getStateForNeighborUpdate(BlockState state, Direction dir, BlockState other_state, WorldAccess world, BlockPos pos, BlockPos moved) { + BlockState new_state = super.getStateForNeighborUpdate(state, dir, other_state, world, pos, moved); + if (dir == Direction.DOWN) return new_state; + + for (Direction side: Direction.Type.HORIZONTAL) { + BlockState neighbor = world.getBlockState(pos.offset(side)); + new_state = new_state.with(getPaneProperty(side), connectsTo( + neighbor, + neighbor.isSideSolidFullSquare(world, pos.offset(side), side.getOpposite()) + )); + } + return new_state; + } + + @Override + public @Nullable BlockState getPlacementState(ItemPlacementContext ctx) { + BlockState state = super.getPlacementState(ctx); + World world = ctx.getWorld(); + BlockPos pos = ctx.getBlockPos(); + + for (Direction dir: Direction.Type.HORIZONTAL) { + BlockState neighbor = world.getBlockState(pos.offset(dir)); + state = state.with(getPaneProperty(dir), connectsTo( + neighbor, + neighbor.isSideSolidFullSquare(world, pos.offset(dir), dir.getOpposite()) + )); + } + return state; + } + + @Override + public void onStateReplaced(BlockState state, World world, BlockPos pos, BlockState newState, boolean moved) { + super.onStateReplaced(state, world, pos, newState, moved); + + if(!state.isOf(newState.getBlock())) world.removeBlockEntity(pos); + } + + @Override + public VoxelShape getOutlineShape(BlockState state, BlockView world, BlockPos pos, ShapeContext context) { + VoxelShape shape = PANE_VOXELS[0]; + for (Direction dir: Direction.Type.HORIZONTAL) { + if (state.get(getPaneProperty(dir))) + shape = VoxelShapes.union(shape, PANE_VOXELS[dir.ordinal() - 1]); + } + return shape; + } + + @Override + public BlockState rotate(BlockState state, BlockRotation rotation) { + return Direction.Type.HORIZONTAL.stream().reduce(state, (s, dir) -> + s.with(getPaneProperty(rotation.rotate(dir)), state.get(getPaneProperty(dir))) + , (prev, next) -> next); + } + + @Override + public BlockState mirror(BlockState state, BlockMirror mirror) { + return Direction.Type.HORIZONTAL.stream().reduce(state, (s, dir) -> + s.with(getPaneProperty(mirror.apply(dir)), state.get(getPaneProperty(dir))) + , (prev, next) -> next); + } + + public static boolean connectsTo(BlockState state, boolean fs) { + return !cannotConnect(state) && fs || state.getBlock() instanceof PaneBlock || state.isIn(BlockTags.WALLS); + } + + public static BooleanProperty getPaneProperty(Direction dir) { + return switch (dir) { + case NORTH -> NORTH; + case EAST -> EAST; + case SOUTH -> SOUTH; + case WEST -> WEST; + default -> null; + }; + } + + static { + VoxelShape POST = createCuboidShape(7, 0, 7, 9, 16, 9); + VoxelShape SIDE = createCuboidShape(7, 0, 0, 9, 16, 7); + PANE_VOXELS = VoxelHelper.VoxelListBuilder.create(POST, 5) + .add(SIDE) + .add(VoxelHelper::mirrorZ) + .add(VoxelHelper::rotateY) + .add(VoxelHelper::mirrorX) + .build(); + } +} diff --git a/src/main/java/fr/adrien1106/reframed/block/ReFramedTrapdoorBlock.java b/src/main/java/fr/adrien1106/reframed/block/ReFramedTrapdoorBlock.java new file mode 100644 index 0000000..d2a2c09 --- /dev/null +++ b/src/main/java/fr/adrien1106/reframed/block/ReFramedTrapdoorBlock.java @@ -0,0 +1,159 @@ +package fr.adrien1106.reframed.block; + +import fr.adrien1106.reframed.util.VoxelHelper; +import net.minecraft.block.*; +import net.minecraft.block.enums.BlockHalf; +import net.minecraft.entity.ai.pathing.NavigationType; +import net.minecraft.entity.player.PlayerEntity; +import net.minecraft.fluid.Fluids; +import net.minecraft.item.ItemPlacementContext; +import net.minecraft.item.ItemStack; +import net.minecraft.sound.SoundCategory; +import net.minecraft.state.StateManager; +import net.minecraft.util.ActionResult; +import net.minecraft.util.BlockMirror; +import net.minecraft.util.BlockRotation; +import net.minecraft.util.Hand; +import net.minecraft.util.hit.BlockHitResult; +import net.minecraft.util.math.BlockPos; +import net.minecraft.util.math.Direction; +import net.minecraft.util.shape.VoxelShape; +import net.minecraft.world.BlockView; +import net.minecraft.world.World; +import net.minecraft.world.event.GameEvent; +import net.minecraft.world.explosion.Explosion; +import org.jetbrains.annotations.Nullable; + +import java.util.function.BiConsumer; + +import static net.minecraft.state.property.Properties.*; + +public class ReFramedTrapdoorBlock extends WaterloggableReFramedBlock { + + public static final VoxelShape[] TRAPDOOR_VOXELS; + + public ReFramedTrapdoorBlock(Settings settings) { + super(settings); + setDefaultState(getDefaultState() + .with(HORIZONTAL_FACING, Direction.NORTH) + .with(BLOCK_HALF, BlockHalf.BOTTOM) + .with(OPEN, false) + .with(POWERED, false) + ); + } + + @Override + protected void appendProperties(StateManager.Builder builder) { + super.appendProperties(builder.add(HORIZONTAL_FACING, BLOCK_HALF, OPEN, POWERED)); + } + + @Override + public void neighborUpdate(BlockState state, World world, BlockPos pos, Block source, BlockPos sourcePos, boolean notify) { + if (world.isClient) return; + boolean powered = world.isReceivingRedstonePower(pos); + if (powered != state.get(POWERED)) { + if (state.get(OPEN) != powered) { + state = state.with(OPEN, powered); + playToggleSound(null, world, pos, powered); + } + + world.setBlockState(pos, state.with(POWERED, powered), 2); + if (state.get(WATERLOGGED)) { + world.scheduleFluidTick(pos, Fluids.WATER, Fluids.WATER.getTickRate(world)); + } + } + } + + @Override + public @Nullable BlockState getPlacementState(ItemPlacementContext ctx) { + BlockState state = super.getPlacementState(ctx); + World world = ctx.getWorld(); + BlockPos pos = ctx.getBlockPos(); + Direction side = ctx.getSide(); + + if (side.getAxis().isVertical()) state = state + .with(HORIZONTAL_FACING, ctx.getHorizontalPlayerFacing().getOpposite()) + .with(BLOCK_HALF, side == Direction.UP ? BlockHalf.BOTTOM : BlockHalf.TOP); + else state = state + .with(HORIZONTAL_FACING, side) + .with(BLOCK_HALF, ctx.getHitPos().getY() - pos.getY() > 0.5 ? BlockHalf.TOP : BlockHalf.BOTTOM); + + if (world.isReceivingRedstonePower(pos)) state = state.with(OPEN, true).with(POWERED, true); + + return state; + } + + @Override + public void onStateReplaced(BlockState state, World world, BlockPos pos, BlockState newState, boolean moved) { + super.onStateReplaced(state, world, pos, newState, moved); + + if(!state.isOf(newState.getBlock())) world.removeBlockEntity(pos); + } + + @Override + public ActionResult onUse(BlockState state, World world, BlockPos pos, PlayerEntity player, Hand hand, BlockHitResult hit) { + ActionResult result = super.onUse(state, world, pos, player, hand, hit); + if (result.isAccepted()) return result; + flip(state, world, pos, player); + return ActionResult.success(world.isClient); + } + + @Override + public boolean canPathfindThrough(BlockState state, BlockView world, BlockPos pos, NavigationType type) { + return switch (type) { + case LAND, AIR -> state.get(OPEN); + case WATER -> state.get(WATERLOGGED); + }; + } + + @Override + public void onExploded(BlockState state, World world, BlockPos pos, Explosion explosion, BiConsumer stack_merger) { + if (explosion.getDestructionType() == Explosion.DestructionType.TRIGGER_BLOCK + && !world.isClient() + && !state.get(POWERED) + ) flip(state, world, pos, null); + + super.onExploded(state, world, pos, explosion, stack_merger); + } + + private void flip(BlockState state, World world, BlockPos pos, @Nullable PlayerEntity player) { + state = state.cycle(OPEN); + world.setBlockState(pos, state, 2); + + this.playToggleSound(player, world, pos, state.get(OPEN)); + } + + protected void playToggleSound(@Nullable PlayerEntity player, World world, BlockPos pos, boolean open) { + world.playSound(player, pos, open ? BlockSetType.OAK.trapdoorOpen() : BlockSetType.OAK.trapdoorClose(), SoundCategory.BLOCKS, 1.0F, world.getRandom().nextFloat() * 0.1F + 0.9F); + world.emitGameEvent(player, open ? GameEvent.BLOCK_OPEN : GameEvent.BLOCK_CLOSE, pos); + } + + @Override + public VoxelShape getOutlineShape(BlockState state, BlockView world, BlockPos pos, ShapeContext context) { + int index; + if (!state.get(OPEN)) index = state.get(BLOCK_HALF) == BlockHalf.BOTTOM ? 0 : 1; + else index = state.get(HORIZONTAL_FACING).ordinal(); + return TRAPDOOR_VOXELS[index]; + } + + @Override + public BlockState rotate(BlockState state, BlockRotation rotation) { + return state.with(HORIZONTAL_FACING, rotation.rotate(state.get(HORIZONTAL_FACING))); + } + + @Override + public BlockState mirror(BlockState state, BlockMirror mirror) { + return state.with(HORIZONTAL_FACING, mirror.apply(state.get(HORIZONTAL_FACING))); + } + + static { + VoxelShape SHAPE = createCuboidShape(0, 0, 0, 16, 3, 16); + TRAPDOOR_VOXELS = VoxelHelper.VoxelListBuilder.create(SHAPE, 6) + .add(VoxelHelper::mirrorY) + .add(VoxelHelper::rotateX) + .add(VoxelHelper::mirrorZ) + .add(VoxelHelper::rotateY) + .add(VoxelHelper::mirrorX) + .build(); + } +} diff --git a/src/main/java/fr/adrien1106/reframed/client/ReFramedClient.java b/src/main/java/fr/adrien1106/reframed/client/ReFramedClient.java index b85aa9a..a4a8520 100644 --- a/src/main/java/fr/adrien1106/reframed/client/ReFramedClient.java +++ b/src/main/java/fr/adrien1106/reframed/client/ReFramedClient.java @@ -117,6 +117,19 @@ public class ReFramedClient implements ClientModInitializer { HELPER.addReFramedModel("pillars_wall_inventory" , HELPER.autoDouble(ReFramed.id("block/pillar"), ReFramed.id("block/wall/full/inventory/sides"))); HELPER.addReFramedModel("pillars_wall_low" , HELPER.autoDouble(ReFramed.id("block/wall/full/pillar/low"), ReFramed.id("block/wall/full/side/low"))); HELPER.addReFramedModel("pillars_wall_tall" , HELPER.autoDouble(ReFramed.id("block/wall/full/pillar/tall"), ReFramed.id("block/wall/full/side/tall"))); + // PANE + HELPER.addReFramedModel("pane_inventory" , HELPER.auto(ReFramed.id("block/pane"))); + HELPER.addReFramedModel("pane_post" , HELPER.auto(new Identifier("block/glass_pane_post"))); + HELPER.addReFramedModel("pane_side" , HELPER.auto(new Identifier("block/glass_pane_side"))); + HELPER.addReFramedModel("pane_side_alt" , HELPER.auto(new Identifier("block/glass_pane_side_alt"))); + HELPER.addReFramedModel("pane_noside" , HELPER.auto(new Identifier("block/glass_pane_noside"))); + HELPER.addReFramedModel("pane_noside_alt" , HELPER.auto(new Identifier("block/glass_pane_noside_alt"))); + // TRAPDOOR + HELPER.addReFramedModel("trapdoor_open" , HELPER.auto(new Identifier("block/oak_trapdoor_open"))); + HELPER.addReFramedModel("trapdoor_bottom" , HELPER.auto(new Identifier("block/oak_trapdoor_bottom"))); + HELPER.addReFramedModel("trapdoor_top" , HELPER.auto(new Identifier("block/oak_trapdoor_top"))); + // DOOR + HELPER.addReFramedModel("door_inventory" , HELPER.auto(ReFramed.id("block/door"))); //item model assignments (in lieu of models/item/___.json) @@ -136,10 +149,13 @@ public class ReFramedClient implements ClientModInitializer { HELPER.assignItemModel("pillar" , ReFramed.PILLAR); HELPER.assignItemModel("pillars_wall_inventory", ReFramed.PILLARS_WALL); HELPER.assignItemModel("wall_inventory" , ReFramed.WALL); + HELPER.assignItemModel("pane_inventory" , ReFramed.PANE); + HELPER.assignItemModel("trapdoor_bottom" , ReFramed.TRAPDOOR); + HELPER.assignItemModel("door_inventory" , ReFramed.DOOR); } private void privateInit() { - //set up some magic to force chunk rerenders when you change a template (see TemplateEntity) + //set up some magic to force chunk re-renders when you change a template (see TemplateEntity) ReFramed.chunkRerenderProxy = (world, pos) -> { if(world == MinecraftClient.getInstance().world) { MinecraftClient.getInstance().worldRenderer.scheduleBlockRender( diff --git a/src/main/java/fr/adrien1106/reframed/generator/GBlockTag.java b/src/main/java/fr/adrien1106/reframed/generator/GBlockTag.java index 33ca890..cae2d8e 100644 --- a/src/main/java/fr/adrien1106/reframed/generator/GBlockTag.java +++ b/src/main/java/fr/adrien1106/reframed/generator/GBlockTag.java @@ -18,6 +18,7 @@ public class GBlockTag extends BlockTagProvider { static { providers.put(ReFramedPillarsWallBlock.class, new PillarsWall()); providers.put(ReFramedWallBlock.class, new Wall()); + providers.put(ReFramedPaneBlock.class, new Pane()); } public GBlockTag(FabricDataOutput output, CompletableFuture registries) { diff --git a/src/main/java/fr/adrien1106/reframed/generator/GBlockstate.java b/src/main/java/fr/adrien1106/reframed/generator/GBlockstate.java index 871b3df..d111af5 100644 --- a/src/main/java/fr/adrien1106/reframed/generator/GBlockstate.java +++ b/src/main/java/fr/adrien1106/reframed/generator/GBlockstate.java @@ -34,6 +34,9 @@ public class GBlockstate extends FabricModelProvider { providers.put(ReFramedStepsSlabBlock.class, new StepsSlab()); providers.put(ReFramedPillarsWallBlock.class, new PillarsWall()); providers.put(ReFramedWallBlock.class, new Wall()); + providers.put(ReFramedPaneBlock.class, new Pane()); + providers.put(ReFramedTrapdoorBlock.class, new Trapdoor()); + providers.put(ReFramedDoorBlock.class, new Door()); } public GBlockstate(FabricDataOutput output) { diff --git a/src/main/java/fr/adrien1106/reframed/generator/GRecipe.java b/src/main/java/fr/adrien1106/reframed/generator/GRecipe.java index 29ddf50..7ac1beb 100644 --- a/src/main/java/fr/adrien1106/reframed/generator/GRecipe.java +++ b/src/main/java/fr/adrien1106/reframed/generator/GRecipe.java @@ -36,6 +36,9 @@ public class GRecipe extends FabricRecipeProvider { providers.put(ReFramedStepsSlabBlock.class, new StepsSlab()); providers.put(ReFramedPillarsWallBlock.class, new PillarsWall()); providers.put(ReFramedWallBlock.class, new Wall()); + providers.put(ReFramedPaneBlock.class, new Pane()); + providers.put(ReFramedTrapdoorBlock.class, new Trapdoor()); + providers.put(ReFramedDoorBlock.class, new Door()); providers.put(ReFramedBlueprintItem.class, new Blueprint()); providers.put(ReFramedHammerItem.class, new Hammer()); providers.put(ReFramedScrewdriverItem.class, new Screwdriver()); diff --git a/src/main/java/fr/adrien1106/reframed/generator/block/Door.java b/src/main/java/fr/adrien1106/reframed/generator/block/Door.java new file mode 100644 index 0000000..81dc6e1 --- /dev/null +++ b/src/main/java/fr/adrien1106/reframed/generator/block/Door.java @@ -0,0 +1,73 @@ +package fr.adrien1106.reframed.generator.block; + +import fr.adrien1106.reframed.ReFramed; +import fr.adrien1106.reframed.generator.BlockStateProvider; +import fr.adrien1106.reframed.generator.GBlockstate; +import fr.adrien1106.reframed.generator.RecipeSetter; +import net.fabricmc.fabric.api.datagen.v1.provider.FabricRecipeProvider; +import net.minecraft.block.Block; +import net.minecraft.data.client.BlockStateSupplier; +import net.minecraft.data.client.MultipartBlockStateSupplier; +import net.minecraft.data.client.When; +import net.minecraft.data.server.recipe.RecipeExporter; +import net.minecraft.data.server.recipe.RecipeProvider; +import net.minecraft.data.server.recipe.ShapedRecipeJsonBuilder; +import net.minecraft.item.ItemConvertible; +import net.minecraft.recipe.book.RecipeCategory; +import net.minecraft.util.Identifier; +import net.minecraft.util.math.Direction; + +import static net.minecraft.block.enums.DoorHinge.*; +import static net.minecraft.data.client.VariantSettings.Rotation.*; +import static net.minecraft.state.property.Properties.*; + +public class Door implements RecipeSetter, BlockStateProvider { + + @Override + public void setRecipe(RecipeExporter exporter, ItemConvertible convertible) { + RecipeProvider.offerStonecuttingRecipe(exporter, RecipeCategory.BUILDING_BLOCKS, convertible, ReFramed.CUBE, 1); + ShapedRecipeJsonBuilder + .create(RecipeCategory.BUILDING_BLOCKS, convertible, 3) + .pattern("II") + .pattern("II") + .pattern("II") + .input('I', ReFramed.CUBE) + .criterion(FabricRecipeProvider.hasItem(ReFramed.CUBE), FabricRecipeProvider.conditionsFromItem(ReFramed.CUBE)) + .criterion(FabricRecipeProvider.hasItem(convertible), FabricRecipeProvider.conditionsFromItem(convertible)) + .offerTo(exporter); + } + + @Override + public BlockStateSupplier getMultipart(Block block) { + Identifier door = ReFramed.id("trapdoor_open_special"); + return MultipartBlockStateSupplier.create(block) + // SOUTH + .with(When.anyOf( + GBlockstate.when(OPEN, false, HORIZONTAL_FACING, Direction.SOUTH), + GBlockstate.when(OPEN, true, HORIZONTAL_FACING, Direction.EAST, DOOR_HINGE, LEFT), + GBlockstate.when(OPEN, true, HORIZONTAL_FACING, Direction.WEST, DOOR_HINGE, RIGHT) + ), + GBlockstate.variant(door, true, R0, R0)) + // WEST + .with(When.anyOf( + GBlockstate.when(OPEN, false, HORIZONTAL_FACING, Direction.WEST), + GBlockstate.when(OPEN, true, HORIZONTAL_FACING, Direction.SOUTH, DOOR_HINGE, LEFT), + GBlockstate.when(OPEN, true, HORIZONTAL_FACING, Direction.NORTH, DOOR_HINGE, RIGHT) + ), + GBlockstate.variant(door, true, R0, R90)) + // NORTH + .with(When.anyOf( + GBlockstate.when(OPEN, false, HORIZONTAL_FACING, Direction.NORTH), + GBlockstate.when(OPEN, true, HORIZONTAL_FACING, Direction.WEST, DOOR_HINGE, LEFT), + GBlockstate.when(OPEN, true, HORIZONTAL_FACING, Direction.EAST, DOOR_HINGE, RIGHT) + ), + GBlockstate.variant(door, true, R0, R180)) + // EAST + .with(When.anyOf( + GBlockstate.when(OPEN, false, HORIZONTAL_FACING, Direction.EAST), + GBlockstate.when(OPEN, true, HORIZONTAL_FACING, Direction.NORTH, DOOR_HINGE, LEFT), + GBlockstate.when(OPEN, true, HORIZONTAL_FACING, Direction.SOUTH, DOOR_HINGE, RIGHT) + ), + GBlockstate.variant(door, true, R0, R270)); + } +} diff --git a/src/main/java/fr/adrien1106/reframed/generator/block/Pane.java b/src/main/java/fr/adrien1106/reframed/generator/block/Pane.java new file mode 100644 index 0000000..38081d8 --- /dev/null +++ b/src/main/java/fr/adrien1106/reframed/generator/block/Pane.java @@ -0,0 +1,76 @@ +package fr.adrien1106.reframed.generator.block; + +import fr.adrien1106.reframed.ReFramed; +import fr.adrien1106.reframed.generator.BlockStateProvider; +import fr.adrien1106.reframed.generator.GBlockstate; +import fr.adrien1106.reframed.generator.RecipeSetter; +import fr.adrien1106.reframed.generator.TagGetter; +import net.fabricmc.fabric.api.datagen.v1.provider.FabricRecipeProvider; +import net.minecraft.block.Block; +import net.minecraft.data.client.BlockStateSupplier; +import net.minecraft.data.client.MultipartBlockStateSupplier; +import net.minecraft.data.server.recipe.RecipeExporter; +import net.minecraft.data.server.recipe.RecipeProvider; +import net.minecraft.data.server.recipe.ShapedRecipeJsonBuilder; +import net.minecraft.item.ItemConvertible; +import net.minecraft.recipe.book.RecipeCategory; +import net.minecraft.registry.tag.BlockTags; +import net.minecraft.registry.tag.TagKey; +import net.minecraft.util.Identifier; + +import java.util.List; + +import static net.minecraft.data.client.VariantSettings.Rotation.*; +import static net.minecraft.state.property.Properties.*; + +public class Pane implements RecipeSetter, TagGetter, BlockStateProvider { + + @Override + public void setRecipe(RecipeExporter exporter, ItemConvertible convertible) { + RecipeProvider.offerStonecuttingRecipe(exporter, RecipeCategory.BUILDING_BLOCKS, convertible, ReFramed.CUBE, 4); + ShapedRecipeJsonBuilder + .create(RecipeCategory.BUILDING_BLOCKS, convertible, 32) + .pattern("III") + .pattern("I I") + .pattern("III") + .input('I', ReFramed.CUBE) + .criterion(FabricRecipeProvider.hasItem(ReFramed.CUBE), FabricRecipeProvider.conditionsFromItem(ReFramed.CUBE)) + .criterion(FabricRecipeProvider.hasItem(convertible), FabricRecipeProvider.conditionsFromItem(convertible)) + .offerTo(exporter); + } + + @Override + public List> getTags() { + return List.of(BlockTags.WALLS); + } + + @Override + public BlockStateSupplier getMultipart(Block block) { + Identifier + pane_side = ReFramed.id("pane_side_special"), + pane_side_alt = ReFramed.id("pane_side_alt_special"), + pane_noside = ReFramed.id("pane_noside_special"), + pane_noside_alt = ReFramed.id("pane_noside_alt_special"); + return MultipartBlockStateSupplier.create(block) + // PILLAR CORE + .with(GBlockstate.variant(ReFramed.id("pane_post_special"), true, R0, R0)) + // SIDE + .with(GBlockstate.when(NORTH, true), + GBlockstate.variant(pane_side, true, R0, R0)) + .with(GBlockstate.when(EAST, true), + GBlockstate.variant(pane_side, true, R0, R90)) + .with(GBlockstate.when(SOUTH, true), + GBlockstate.variant(pane_side_alt, true, R0, R0)) + .with(GBlockstate.when(WEST, true), + GBlockstate.variant(pane_side_alt, true, R0, R90)) + // NOSIDE + .with(GBlockstate.when(NORTH, false), + GBlockstate.variant(pane_noside, true, R0, R0)) + .with(GBlockstate.when(EAST, false), + GBlockstate.variant(pane_noside_alt, true, R0, R0)) + .with(GBlockstate.when(SOUTH, false), + GBlockstate.variant(pane_noside_alt, true, R0, R90)) + .with(GBlockstate.when(WEST, false), + GBlockstate.variant(pane_noside, true, R0, R270)); + } +} diff --git a/src/main/java/fr/adrien1106/reframed/generator/block/Trapdoor.java b/src/main/java/fr/adrien1106/reframed/generator/block/Trapdoor.java new file mode 100644 index 0000000..50d4689 --- /dev/null +++ b/src/main/java/fr/adrien1106/reframed/generator/block/Trapdoor.java @@ -0,0 +1,56 @@ +package fr.adrien1106.reframed.generator.block; + +import fr.adrien1106.reframed.ReFramed; +import fr.adrien1106.reframed.generator.BlockStateProvider; +import fr.adrien1106.reframed.generator.GBlockstate; +import fr.adrien1106.reframed.generator.RecipeSetter; +import net.fabricmc.fabric.api.datagen.v1.provider.FabricRecipeProvider; +import net.minecraft.block.Block; +import net.minecraft.block.enums.BlockHalf; +import net.minecraft.data.client.BlockStateSupplier; +import net.minecraft.data.client.MultipartBlockStateSupplier; +import net.minecraft.data.server.recipe.RecipeExporter; +import net.minecraft.data.server.recipe.RecipeProvider; +import net.minecraft.data.server.recipe.ShapedRecipeJsonBuilder; +import net.minecraft.item.ItemConvertible; +import net.minecraft.recipe.book.RecipeCategory; +import net.minecraft.util.Identifier; +import net.minecraft.util.math.Direction; + +import static net.minecraft.data.client.VariantSettings.Rotation.*; +import static net.minecraft.state.property.Properties.*; + +public class Trapdoor implements RecipeSetter, BlockStateProvider { + + @Override + public void setRecipe(RecipeExporter exporter, ItemConvertible convertible) { + RecipeProvider.offerStonecuttingRecipe(exporter, RecipeCategory.BUILDING_BLOCKS, convertible, ReFramed.CUBE, 2); + ShapedRecipeJsonBuilder + .create(RecipeCategory.BUILDING_BLOCKS, convertible, 2) + .pattern(" I") + .pattern("III") + .pattern("II ") + .input('I', ReFramed.CUBE) + .criterion(FabricRecipeProvider.hasItem(ReFramed.CUBE), FabricRecipeProvider.conditionsFromItem(ReFramed.CUBE)) + .criterion(FabricRecipeProvider.hasItem(convertible), FabricRecipeProvider.conditionsFromItem(convertible)) + .offerTo(exporter); + } + + @Override + public BlockStateSupplier getMultipart(Block block) { + Identifier open = ReFramed.id("trapdoor_open_special"); + return MultipartBlockStateSupplier.create(block) + .with(GBlockstate.when(OPEN, false, BLOCK_HALF, BlockHalf.BOTTOM), + GBlockstate.variant(ReFramed.id("trapdoor_bottom_special"), true, R0, R0)) + .with(GBlockstate.when(OPEN, false, BLOCK_HALF, BlockHalf.TOP), + GBlockstate.variant(ReFramed.id("trapdoor_top_special"), true, R0, R0)) + .with(GBlockstate.when(OPEN, true, HORIZONTAL_FACING, Direction.NORTH), + GBlockstate.variant(open, true, R0, R0)) + .with(GBlockstate.when(OPEN, true, HORIZONTAL_FACING, Direction.EAST), + GBlockstate.variant(open, true, R0, R90)) + .with(GBlockstate.when(OPEN, true, HORIZONTAL_FACING, Direction.SOUTH), + GBlockstate.variant(open, true, R0, R180)) + .with(GBlockstate.when(OPEN, true, HORIZONTAL_FACING, Direction.WEST), + GBlockstate.variant(open, true, R0, R270)); + } +} diff --git a/src/main/resources/assets/reframed/models/block/door.json b/src/main/resources/assets/reframed/models/block/door.json new file mode 100644 index 0000000..4c70e48 --- /dev/null +++ b/src/main/resources/assets/reframed/models/block/door.json @@ -0,0 +1,34 @@ +{ + "credit": "Made with Blockbench", + "parent": "block/block", + "textures": { + "particle": "#side" + }, + "elements": [ + { + "from": [14, 0, 0], + "to": [16, 16, 16], + "rotation": {"angle": 0, "axis": "y", "origin": [7, 0, 0]}, + "faces": { + "north": {"uv": [0, 0, 2, 16], "texture": "#side", "cullface": "north"}, + "east": {"uv": [0, 0, 16, 16], "texture": "#side", "cullface": "east"}, + "south": {"uv": [14, 0, 16, 16], "texture": "#side", "cullface": "south"}, + "west": {"uv": [0, 0, 16, 16], "texture": "#side"}, + "down": {"uv": [14, 0, 16, 16], "texture": "#bottom", "cullface": "down"} + } + }, + { + "from": [14, 16, 0], + "to": [16, 32, 16], + "rotation": {"angle": 0, "axis": "y", "origin": [7, 16, 0]}, + "faces": { + "north": {"uv": [0, 0, 2, 0], "texture": "#side", "cullface": "north"}, + "east": {"uv": [0, 0, 16, 16], "texture": "#side", "cullface": "east"}, + "south": {"uv": [14, 0, 16, 0], "texture": "#side", "cullface": "south"}, + "west": {"uv": [0, 0, 16, 16], "texture": "#side"}, + "up": {"uv": [14, 0, 16, 16], "texture": "#top", "cullface": "up"} + } + } + ], + "display": {} +} \ No newline at end of file diff --git a/src/main/resources/assets/reframed/models/block/pane.json b/src/main/resources/assets/reframed/models/block/pane.json new file mode 100644 index 0000000..117fc7b --- /dev/null +++ b/src/main/resources/assets/reframed/models/block/pane.json @@ -0,0 +1,21 @@ +{ + "credit": "Made with Blockbench", + "parent": "block/block", + "textures": { + "particle": "#side" + }, + "elements": [ + { + "from": [7, 0, 0], + "to": [9, 16, 16], + "faces": { + "north": {"uv": [7, 0, 9, 16], "texture": "#side", "cullface": "north"}, + "east": {"uv": [0, 0, 16, 16], "texture": "#side"}, + "south": {"uv": [7, 0, 9, 16], "texture": "#side", "cullface": "south"}, + "west": {"uv": [0, 0, 16, 16], "texture": "#side"}, + "up": {"uv": [7, 0, 9, 16], "texture": "#top", "cullface": "up"}, + "down": {"uv": [7, 0, 9, 16], "texture": "#bottom", "cullface": "down"} + } + } + ] +} \ No newline at end of file