Simplify slope placement a lot
This commit is contained in:
parent
3c6e6399f2
commit
9698c61810
@ -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
|
||||
|
||||
|
@ -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();
|
||||
|
||||
|
@ -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();
|
||||
}
|
||||
}
|
@ -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;
|
||||
}
|
||||
}
|
||||
|
@ -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;
|
||||
}
|
||||
|
@ -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;
|
||||
}
|
||||
}
|
||||
|
@ -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);
|
||||
}
|
||||
public static Edge stairslikePlacement(ItemPlacementContext ctx) {
|
||||
Direction playerHorizontalFacing = ctx.getHorizontalPlayerFacing();
|
||||
Direction clickedFace = ctx.getSide();
|
||||
boolean sneaky = ctx.getPlayer() != null && ctx.getPlayer().isSneaky();
|
||||
|
||||
private final byte key;
|
||||
double dx = ctx.getHitPos().x - ctx.getBlockPos().getX();
|
||||
double dy = ctx.getHitPos().y - ctx.getBlockPos().getY();
|
||||
double dz = ctx.getHitPos().z - ctx.getBlockPos().getZ();
|
||||
|
||||
private static final Byte2ObjectMap<Edge> LOOKUP = new Byte2ObjectOpenHashMap<>();
|
||||
static { for(Edge e : values()) LOOKUP.put(e.key, e); }
|
||||
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();
|
||||
};
|
||||
|
||||
private static byte key(AxisRelation x, AxisRelation y, AxisRelation z) {
|
||||
return (byte) (x.ordinal() + 1 << 4 | y.ordinal() + 1 << 2 | z.ordinal() + 1);
|
||||
}
|
||||
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();
|
||||
};
|
||||
|
||||
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();
|
||||
|
||||
//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);
|
||||
|
||||
//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);
|
||||
|
||||
//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);
|
||||
}
|
||||
|
||||
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;
|
||||
}
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user