diff --git a/README.md b/README.md index 4758fbe..74b38ab 100644 --- a/README.md +++ b/README.md @@ -24,9 +24,13 @@ Todo move this into the main readme section ## Creating your block -So there are various block interactions in Templates, like adding glowstone to make the block illuminate, adding redstone to make it a power source, etc. In an ideal world, I'd be able to simply provide the `TemplateBlock` class implementing all those features and ask that you extend it from your block. And if your block doesn't already have a superclass I recommend doing this - alas, if it does you can't extend `TemplateBlock`, so the implementation of everything has been farmed out to `public static` methods in `TemplateInteractionUtil` in the api package. Simply wire everything up. Don't forget to feed your `Block.Settings` through `TemplateInteractionUtil.configureSettings` too. +So there are various block interactions in Templates, like adding glowstone to make the block illuminate, adding redstone to make it a power source, etc. -The only other requirement is that your block *must* have a block entity that both returns a `BlockState` object from `RenderAttachmentBlockEntity.getRenderAttachmentData` and implements `ThemeableBlockEntity`. `TemplateEntity` is an implementation of this (and also implements the other half of the features from `TemplateInteractionUtil`); I recommend using it if you can. +In an ideal world, I'd be able to simply provide the `TemplateBlock` class implementing all those features and ask that you extend it from your block. If your block doesn't already have a superclass I recommend doing this - alas, if it does, you can't also extend `TemplateBlock`, so the implementation of everything has been farmed out to `public static` methods in `TemplateInteractionUtil` in the api package. Simply wire everything up. + +Don't forget to feed your `Block.Settings` through `TemplateInteractionUtil.configureSettings` too, and if there's any behaviors you'd like to tweak, try implementing some methods from `TemplateInteractionUtilExt` before resorting to copy-pasting the implementation of `TemplateInteractionUtil` methods. But copy paste away if you must. + +The only other requirement is that your block *must* have a block entity, that both returns a `BlockState` object from `RenderAttachmentBlockEntity.getRenderAttachmentData` and implements `ThemeableBlockEntity`. `TemplateEntity` is an implementation of this (and also implements the other half of the features from `TemplateInteractionUtil`); I recommend using it if you can. ## Creating the custom model diff --git a/src/main/java/io/github/cottonmc/templates/api/TemplateInteractionUtil.java b/src/main/java/io/github/cottonmc/templates/api/TemplateInteractionUtil.java index 4058cb0..fec4bdf 100644 --- a/src/main/java/io/github/cottonmc/templates/api/TemplateInteractionUtil.java +++ b/src/main/java/io/github/cottonmc/templates/api/TemplateInteractionUtil.java @@ -59,6 +59,7 @@ public class TemplateInteractionUtil { if(!player.canModifyBlocks() || !world.canPlayerModifyAt(player, pos)) return ActionResult.PASS; ItemStack held = player.getStackInHand(hand); + TemplateInteractionUtilExt ext = state.getBlock() instanceof TemplateInteractionUtilExt e ? e : TemplateInteractionUtilExt.Default.INSTANCE; //Glowstone if(state.contains(LIGHT) && held.getItem() == Items.GLOWSTONE_DUST && !state.get(LIGHT) && !be.hasSpentGlowstoneDust()) { @@ -71,7 +72,11 @@ public class TemplateInteractionUtil { } //Redstone - if(held.getItem() == Blocks.REDSTONE_TORCH.asItem() && !be.emitsRedstone() && !be.hasSpentRedstoneTorch()) { + if(held.getItem() == Blocks.REDSTONE_TORCH.asItem() && + !be.emitsRedstone() && + !be.hasSpentRedstoneTorch() && + ext.templatesPlayerCanAddRedstoneEmission(state, world, pos) + ) { be.setEmitsRedstone(true); be.spentRedstoneTorch(); @@ -81,7 +86,11 @@ public class TemplateInteractionUtil { } //Popped chorus fruit - if(held.getItem() == Items.POPPED_CHORUS_FRUIT && be.isSolid() && !be.hasSpentPoppedChorus()) { + if(held.getItem() == Items.POPPED_CHORUS_FRUIT && + be.isSolid() && + !be.hasSpentPoppedChorus() && + ext.templatesPlayerCanRemoveCollision(state, world, pos) + ) { be.setSolidity(false); be.spentPoppedChorus(); diff --git a/src/main/java/io/github/cottonmc/templates/api/TemplateInteractionUtilExt.java b/src/main/java/io/github/cottonmc/templates/api/TemplateInteractionUtilExt.java new file mode 100644 index 0000000..b1d21e5 --- /dev/null +++ b/src/main/java/io/github/cottonmc/templates/api/TemplateInteractionUtilExt.java @@ -0,0 +1,20 @@ +package io.github.cottonmc.templates.api; + +import net.minecraft.block.BlockState; +import net.minecraft.util.math.BlockPos; +import net.minecraft.util.math.Direction; +import net.minecraft.world.BlockView; + +public interface TemplateInteractionUtilExt { + default boolean templatesPlayerCanAddRedstoneEmission(BlockState state, BlockView view, BlockPos pos) { + return state.getWeakRedstonePower(view, pos, Direction.UP) == 0; + } + + default boolean templatesPlayerCanRemoveCollision(BlockState state, BlockView view, BlockPos pos) { + return !state.getCollisionShape(view, pos).isEmpty(); + } + + class Default implements TemplateInteractionUtilExt { + public static final Default INSTANCE = new Default(); + } +} diff --git a/src/main/java/io/github/cottonmc/templates/block/TemplateFenceGateBlock.java b/src/main/java/io/github/cottonmc/templates/block/TemplateFenceGateBlock.java index acf36a7..0478b88 100644 --- a/src/main/java/io/github/cottonmc/templates/block/TemplateFenceGateBlock.java +++ b/src/main/java/io/github/cottonmc/templates/block/TemplateFenceGateBlock.java @@ -3,6 +3,7 @@ package io.github.cottonmc.templates.block; import com.google.common.base.MoreObjects; import io.github.cottonmc.templates.Templates; import io.github.cottonmc.templates.api.TemplateInteractionUtil; +import io.github.cottonmc.templates.api.TemplateInteractionUtilExt; import net.minecraft.block.Block; import net.minecraft.block.BlockEntityProvider; import net.minecraft.block.BlockState; @@ -24,7 +25,7 @@ import net.minecraft.world.BlockView; import net.minecraft.world.World; import org.jetbrains.annotations.Nullable; -public class TemplateFenceGateBlock extends FenceGateBlock implements BlockEntityProvider { +public class TemplateFenceGateBlock extends FenceGateBlock implements BlockEntityProvider, TemplateInteractionUtilExt { public TemplateFenceGateBlock(Settings settings, WoodType woodType) { super(settings, woodType); setDefaultState(TemplateInteractionUtil.setDefaultStates(getDefaultState())); @@ -82,4 +83,9 @@ public class TemplateFenceGateBlock extends FenceGateBlock implements BlockEntit public int getStrongRedstonePower(BlockState state, BlockView view, BlockPos pos, Direction dir) { return TemplateInteractionUtil.getStrongRedstonePower(state, view, pos, dir); } + + @Override + public boolean templatesPlayerCanAddRedstoneEmission(BlockState state, BlockView view, BlockPos pos) { + return false; + } } diff --git a/src/main/java/io/github/cottonmc/templates/block/TemplateSlopeBlock.java b/src/main/java/io/github/cottonmc/templates/block/TemplateSlopeBlock.java index 1022ab9..cafd5b0 100644 --- a/src/main/java/io/github/cottonmc/templates/block/TemplateSlopeBlock.java +++ b/src/main/java/io/github/cottonmc/templates/block/TemplateSlopeBlock.java @@ -40,7 +40,7 @@ public class TemplateSlopeBlock extends WaterloggableTemplateBlock { @Override public BlockState getPlacementState(ItemPlacementContext ctx) { BlockState sup = super.getPlacementState(ctx); - if(sup != null) sup = sup.with(EDGE, Edge.closestTo(ctx.getHitPos(), ctx.getBlockPos())); + if(sup != null) sup = sup.with(EDGE, Edge.stairslikePlacement(ctx)); return sup; } diff --git a/src/main/java/io/github/cottonmc/templates/block/TemplateTrapdoorBlock.java b/src/main/java/io/github/cottonmc/templates/block/TemplateTrapdoorBlock.java index 7e20590..1c12f99 100644 --- a/src/main/java/io/github/cottonmc/templates/block/TemplateTrapdoorBlock.java +++ b/src/main/java/io/github/cottonmc/templates/block/TemplateTrapdoorBlock.java @@ -3,6 +3,7 @@ package io.github.cottonmc.templates.block; import com.google.common.base.MoreObjects; import io.github.cottonmc.templates.Templates; import io.github.cottonmc.templates.api.TemplateInteractionUtil; +import io.github.cottonmc.templates.api.TemplateInteractionUtilExt; import net.minecraft.block.Block; import net.minecraft.block.BlockEntityProvider; import net.minecraft.block.BlockSetType; @@ -24,7 +25,7 @@ import net.minecraft.world.BlockView; import net.minecraft.world.World; import org.jetbrains.annotations.Nullable; -public class TemplateTrapdoorBlock extends TrapdoorBlock implements BlockEntityProvider { +public class TemplateTrapdoorBlock extends TrapdoorBlock implements BlockEntityProvider, TemplateInteractionUtilExt { public TemplateTrapdoorBlock(Settings settings, BlockSetType blah) { super(settings, blah); setDefaultState(TemplateInteractionUtil.setDefaultStates(getDefaultState())); @@ -82,4 +83,9 @@ public class TemplateTrapdoorBlock extends TrapdoorBlock implements BlockEntityP public int getStrongRedstonePower(BlockState state, BlockView view, BlockPos pos, Direction dir) { return TemplateInteractionUtil.getStrongRedstonePower(state, view, pos, dir); } + + @Override + public boolean templatesPlayerCanAddRedstoneEmission(BlockState state, BlockView view, BlockPos pos) { + return false; + } } diff --git a/src/main/java/io/github/cottonmc/templates/util/Edge.java b/src/main/java/io/github/cottonmc/templates/util/Edge.java index c8ba996..674b630 100644 --- a/src/main/java/io/github/cottonmc/templates/util/Edge.java +++ b/src/main/java/io/github/cottonmc/templates/util/Edge.java @@ -1,78 +1,58 @@ package io.github.cottonmc.templates.util; -import it.unimi.dsi.fastutil.bytes.Byte2ObjectMap; -import it.unimi.dsi.fastutil.bytes.Byte2ObjectOpenHashMap; -import net.minecraft.util.Pair; +import net.minecraft.item.ItemPlacementContext; import net.minecraft.util.StringIdentifiable; -import net.minecraft.util.math.BlockPos; -import net.minecraft.util.math.Vec3d; +import net.minecraft.util.math.Direction; import org.joml.Vector3d; import java.util.Locale; public enum Edge implements StringIdentifiable { - DOWN_NORTH(AxisRelation.PARALLEL, AxisRelation.LOW_SIDE, AxisRelation.LOW_SIDE), - DOWN_SOUTH(AxisRelation.PARALLEL, AxisRelation.LOW_SIDE, AxisRelation.HIGH_SIDE), - UP_SOUTH(AxisRelation.PARALLEL, AxisRelation.HIGH_SIDE, AxisRelation.HIGH_SIDE), - UP_NORTH(AxisRelation.PARALLEL, AxisRelation.HIGH_SIDE, AxisRelation.LOW_SIDE), - NORTH_WEST(AxisRelation.LOW_SIDE, AxisRelation.PARALLEL, AxisRelation.LOW_SIDE), - SOUTH_WEST(AxisRelation.LOW_SIDE, AxisRelation.PARALLEL, AxisRelation.HIGH_SIDE), - SOUTH_EAST(AxisRelation.HIGH_SIDE, AxisRelation.PARALLEL, AxisRelation.HIGH_SIDE), - NORTH_EAST(AxisRelation.HIGH_SIDE, AxisRelation.PARALLEL, AxisRelation.LOW_SIDE), - DOWN_WEST(AxisRelation.LOW_SIDE, AxisRelation.LOW_SIDE, AxisRelation.PARALLEL), - UP_WEST(AxisRelation.LOW_SIDE, AxisRelation.HIGH_SIDE, AxisRelation.PARALLEL), - UP_EAST(AxisRelation.HIGH_SIDE, AxisRelation.HIGH_SIDE, AxisRelation.PARALLEL), - DOWN_EAST(AxisRelation.HIGH_SIDE, AxisRelation.LOW_SIDE, AxisRelation.PARALLEL), - ; + DOWN_NORTH, + DOWN_SOUTH, + UP_SOUTH, + UP_NORTH, + NORTH_WEST, + SOUTH_WEST, + SOUTH_EAST, + NORTH_EAST, + DOWN_WEST, + UP_WEST, + UP_EAST, + DOWN_EAST; - Edge(AxisRelation x, AxisRelation y, AxisRelation z) { - this.key = key(x, y, z); - } - - private final byte key; - - private static final Byte2ObjectMap LOOKUP = new Byte2ObjectOpenHashMap<>(); - static { for(Edge e : values()) LOOKUP.put(e.key, e); } - - private static byte key(AxisRelation x, AxisRelation y, AxisRelation z) { - return (byte) (x.ordinal() + 1 << 4 | y.ordinal() + 1 << 2 | z.ordinal() + 1); - } - - public static Edge closestTo(Vec3d precise, BlockPos coarse) { - double dx = precise.x - coarse.getX(); - double dy = precise.y - coarse.getY(); - double dz = precise.z - coarse.getZ(); + public static Edge stairslikePlacement(ItemPlacementContext ctx) { + Direction playerHorizontalFacing = ctx.getHorizontalPlayerFacing(); + Direction clickedFace = ctx.getSide(); + boolean sneaky = ctx.getPlayer() != null && ctx.getPlayer().isSneaky(); - //distances your click was from the 6 faces of the block (each 0..1) - float distToLowX = (float) (dx); - float distToHighX = (float) (1 - dx); - float distToLowY = (float) (dy); - float distToHighY = (float) (1 - dy); - float distToLowZ = (float) (dz); - float distToHighZ = (float) (1 - dz); + double dx = ctx.getHitPos().x - ctx.getBlockPos().getX(); + double dy = ctx.getHitPos().y - ctx.getBlockPos().getY(); + double dz = ctx.getHitPos().z - ctx.getBlockPos().getZ(); - //distances your click was from either pair of edges (each 0..5) - float distToAnyX = Math.min(distToLowX, distToHighX); - float distToAnyY = Math.min(distToLowY, distToHighY); - float distToAnyZ = Math.min(distToLowZ, distToHighZ); + if(clickedFace == Direction.UP || (!sneaky && dy <= 0.5)) return switch(playerHorizontalFacing) { + case NORTH -> DOWN_NORTH; + case EAST -> DOWN_EAST; + case SOUTH -> DOWN_SOUTH; + case WEST -> DOWN_WEST; + default -> throw new IllegalArgumentException(); + }; - //figure out which two differences are the smallest - AxisRelation clickX, clickY, clickZ; - if((distToAnyX < distToAnyZ) && (distToAnyY < distToAnyZ)) { - clickX = lowOrHigh(distToLowX); - clickY = lowOrHigh(distToLowY); - clickZ = AxisRelation.PARALLEL; - } else if((distToAnyX < distToAnyY) && (distToAnyZ < distToAnyY)) { - clickX = lowOrHigh(distToLowX); - clickY = AxisRelation.PARALLEL; - clickZ = lowOrHigh(distToLowZ); - } else { - clickX = AxisRelation.PARALLEL; - clickY = lowOrHigh(distToLowY); - clickZ = lowOrHigh(distToLowZ); - } + else if(clickedFace == Direction.DOWN || (!sneaky && dy >= 0.5)) return switch(playerHorizontalFacing) { + case NORTH -> UP_NORTH; + case EAST -> UP_EAST; + case SOUTH -> UP_SOUTH; + case WEST -> UP_WEST; + default -> throw new IllegalArgumentException(); + }; - return LOOKUP.getOrDefault(key(clickX, clickY, clickZ), DOWN_SOUTH); + else return switch(playerHorizontalFacing) { + case NORTH -> dx < 0.5 ? Edge.NORTH_WEST : Edge.NORTH_EAST; + case EAST -> dz < 0.5 ? Edge.NORTH_EAST : Edge.SOUTH_EAST; + case SOUTH -> dx > 0.5 ? Edge.SOUTH_EAST : Edge.SOUTH_WEST; + case WEST -> dz > 0.5 ? Edge.SOUTH_WEST : Edge.NORTH_WEST; + default -> throw new IllegalArgumentException(); + }; } //I may have skill issue @@ -106,18 +86,4 @@ public enum Edge implements StringIdentifiable { public String asString() { return name().toLowerCase(Locale.ROOT); } - - //if you imagine moving along this edge, your coordinates: - public enum AxisRelation { - //change along this axis - PARALLEL, - //stay fixed along this axis, with the coordinate being the more negative possibility - LOW_SIDE, - //stay fixed along this axis, with the coordinate being the more positive possibility - HIGH_SIDE - } - - private static AxisRelation lowOrHigh(float distToLow) { - return (distToLow < 0.5f) ? AxisRelation.LOW_SIDE : AxisRelation.HIGH_SIDE; - } }