Simplify slope placement a lot

This commit is contained in:
quat1024 2023-07-11 02:30:51 -04:00
parent 3c6e6399f2
commit 9698c61810
7 changed files with 94 additions and 83 deletions

View File

@ -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

View File

@ -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();

View File

@ -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();
}
}

View File

@ -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;
}
}

View File

@ -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;
}

View File

@ -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;
}
}

View File

@ -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;
}
}