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
|
## 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
|
## Creating the custom model
|
||||||
|
|
||||||
|
@ -59,6 +59,7 @@ public class TemplateInteractionUtil {
|
|||||||
if(!player.canModifyBlocks() || !world.canPlayerModifyAt(player, pos)) return ActionResult.PASS;
|
if(!player.canModifyBlocks() || !world.canPlayerModifyAt(player, pos)) return ActionResult.PASS;
|
||||||
|
|
||||||
ItemStack held = player.getStackInHand(hand);
|
ItemStack held = player.getStackInHand(hand);
|
||||||
|
TemplateInteractionUtilExt ext = state.getBlock() instanceof TemplateInteractionUtilExt e ? e : TemplateInteractionUtilExt.Default.INSTANCE;
|
||||||
|
|
||||||
//Glowstone
|
//Glowstone
|
||||||
if(state.contains(LIGHT) && held.getItem() == Items.GLOWSTONE_DUST && !state.get(LIGHT) && !be.hasSpentGlowstoneDust()) {
|
if(state.contains(LIGHT) && held.getItem() == Items.GLOWSTONE_DUST && !state.get(LIGHT) && !be.hasSpentGlowstoneDust()) {
|
||||||
@ -71,7 +72,11 @@ public class TemplateInteractionUtil {
|
|||||||
}
|
}
|
||||||
|
|
||||||
//Redstone
|
//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.setEmitsRedstone(true);
|
||||||
be.spentRedstoneTorch();
|
be.spentRedstoneTorch();
|
||||||
|
|
||||||
@ -81,7 +86,11 @@ public class TemplateInteractionUtil {
|
|||||||
}
|
}
|
||||||
|
|
||||||
//Popped chorus fruit
|
//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.setSolidity(false);
|
||||||
be.spentPoppedChorus();
|
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 com.google.common.base.MoreObjects;
|
||||||
import io.github.cottonmc.templates.Templates;
|
import io.github.cottonmc.templates.Templates;
|
||||||
import io.github.cottonmc.templates.api.TemplateInteractionUtil;
|
import io.github.cottonmc.templates.api.TemplateInteractionUtil;
|
||||||
|
import io.github.cottonmc.templates.api.TemplateInteractionUtilExt;
|
||||||
import net.minecraft.block.Block;
|
import net.minecraft.block.Block;
|
||||||
import net.minecraft.block.BlockEntityProvider;
|
import net.minecraft.block.BlockEntityProvider;
|
||||||
import net.minecraft.block.BlockState;
|
import net.minecraft.block.BlockState;
|
||||||
@ -24,7 +25,7 @@ import net.minecraft.world.BlockView;
|
|||||||
import net.minecraft.world.World;
|
import net.minecraft.world.World;
|
||||||
import org.jetbrains.annotations.Nullable;
|
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) {
|
public TemplateFenceGateBlock(Settings settings, WoodType woodType) {
|
||||||
super(settings, woodType);
|
super(settings, woodType);
|
||||||
setDefaultState(TemplateInteractionUtil.setDefaultStates(getDefaultState()));
|
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) {
|
public int getStrongRedstonePower(BlockState state, BlockView view, BlockPos pos, Direction dir) {
|
||||||
return TemplateInteractionUtil.getStrongRedstonePower(state, view, pos, 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
|
@Override
|
||||||
public BlockState getPlacementState(ItemPlacementContext ctx) {
|
public BlockState getPlacementState(ItemPlacementContext ctx) {
|
||||||
BlockState sup = super.getPlacementState(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;
|
return sup;
|
||||||
}
|
}
|
||||||
|
@ -3,6 +3,7 @@ package io.github.cottonmc.templates.block;
|
|||||||
import com.google.common.base.MoreObjects;
|
import com.google.common.base.MoreObjects;
|
||||||
import io.github.cottonmc.templates.Templates;
|
import io.github.cottonmc.templates.Templates;
|
||||||
import io.github.cottonmc.templates.api.TemplateInteractionUtil;
|
import io.github.cottonmc.templates.api.TemplateInteractionUtil;
|
||||||
|
import io.github.cottonmc.templates.api.TemplateInteractionUtilExt;
|
||||||
import net.minecraft.block.Block;
|
import net.minecraft.block.Block;
|
||||||
import net.minecraft.block.BlockEntityProvider;
|
import net.minecraft.block.BlockEntityProvider;
|
||||||
import net.minecraft.block.BlockSetType;
|
import net.minecraft.block.BlockSetType;
|
||||||
@ -24,7 +25,7 @@ import net.minecraft.world.BlockView;
|
|||||||
import net.minecraft.world.World;
|
import net.minecraft.world.World;
|
||||||
import org.jetbrains.annotations.Nullable;
|
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) {
|
public TemplateTrapdoorBlock(Settings settings, BlockSetType blah) {
|
||||||
super(settings, blah);
|
super(settings, blah);
|
||||||
setDefaultState(TemplateInteractionUtil.setDefaultStates(getDefaultState()));
|
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) {
|
public int getStrongRedstonePower(BlockState state, BlockView view, BlockPos pos, Direction dir) {
|
||||||
return TemplateInteractionUtil.getStrongRedstonePower(state, view, pos, 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;
|
package io.github.cottonmc.templates.util;
|
||||||
|
|
||||||
import it.unimi.dsi.fastutil.bytes.Byte2ObjectMap;
|
import net.minecraft.item.ItemPlacementContext;
|
||||||
import it.unimi.dsi.fastutil.bytes.Byte2ObjectOpenHashMap;
|
|
||||||
import net.minecraft.util.Pair;
|
|
||||||
import net.minecraft.util.StringIdentifiable;
|
import net.minecraft.util.StringIdentifiable;
|
||||||
import net.minecraft.util.math.BlockPos;
|
import net.minecraft.util.math.Direction;
|
||||||
import net.minecraft.util.math.Vec3d;
|
|
||||||
import org.joml.Vector3d;
|
import org.joml.Vector3d;
|
||||||
|
|
||||||
import java.util.Locale;
|
import java.util.Locale;
|
||||||
|
|
||||||
public enum Edge implements StringIdentifiable {
|
public enum Edge implements StringIdentifiable {
|
||||||
DOWN_NORTH(AxisRelation.PARALLEL, AxisRelation.LOW_SIDE, AxisRelation.LOW_SIDE),
|
DOWN_NORTH,
|
||||||
DOWN_SOUTH(AxisRelation.PARALLEL, AxisRelation.LOW_SIDE, AxisRelation.HIGH_SIDE),
|
DOWN_SOUTH,
|
||||||
UP_SOUTH(AxisRelation.PARALLEL, AxisRelation.HIGH_SIDE, AxisRelation.HIGH_SIDE),
|
UP_SOUTH,
|
||||||
UP_NORTH(AxisRelation.PARALLEL, AxisRelation.HIGH_SIDE, AxisRelation.LOW_SIDE),
|
UP_NORTH,
|
||||||
NORTH_WEST(AxisRelation.LOW_SIDE, AxisRelation.PARALLEL, AxisRelation.LOW_SIDE),
|
NORTH_WEST,
|
||||||
SOUTH_WEST(AxisRelation.LOW_SIDE, AxisRelation.PARALLEL, AxisRelation.HIGH_SIDE),
|
SOUTH_WEST,
|
||||||
SOUTH_EAST(AxisRelation.HIGH_SIDE, AxisRelation.PARALLEL, AxisRelation.HIGH_SIDE),
|
SOUTH_EAST,
|
||||||
NORTH_EAST(AxisRelation.HIGH_SIDE, AxisRelation.PARALLEL, AxisRelation.LOW_SIDE),
|
NORTH_EAST,
|
||||||
DOWN_WEST(AxisRelation.LOW_SIDE, AxisRelation.LOW_SIDE, AxisRelation.PARALLEL),
|
DOWN_WEST,
|
||||||
UP_WEST(AxisRelation.LOW_SIDE, AxisRelation.HIGH_SIDE, AxisRelation.PARALLEL),
|
UP_WEST,
|
||||||
UP_EAST(AxisRelation.HIGH_SIDE, AxisRelation.HIGH_SIDE, AxisRelation.PARALLEL),
|
UP_EAST,
|
||||||
DOWN_EAST(AxisRelation.HIGH_SIDE, AxisRelation.LOW_SIDE, AxisRelation.PARALLEL),
|
DOWN_EAST;
|
||||||
;
|
|
||||||
|
|
||||||
Edge(AxisRelation x, AxisRelation y, AxisRelation z) {
|
public static Edge stairslikePlacement(ItemPlacementContext ctx) {
|
||||||
this.key = key(x, y, z);
|
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<>();
|
if(clickedFace == Direction.UP || (!sneaky && dy <= 0.5)) return switch(playerHorizontalFacing) {
|
||||||
static { for(Edge e : values()) LOOKUP.put(e.key, e); }
|
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) {
|
else if(clickedFace == Direction.DOWN || (!sneaky && dy >= 0.5)) return switch(playerHorizontalFacing) {
|
||||||
return (byte) (x.ordinal() + 1 << 4 | y.ordinal() + 1 << 2 | z.ordinal() + 1);
|
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) {
|
else return switch(playerHorizontalFacing) {
|
||||||
double dx = precise.x - coarse.getX();
|
case NORTH -> dx < 0.5 ? Edge.NORTH_WEST : Edge.NORTH_EAST;
|
||||||
double dy = precise.y - coarse.getY();
|
case EAST -> dz < 0.5 ? Edge.NORTH_EAST : Edge.SOUTH_EAST;
|
||||||
double dz = precise.z - coarse.getZ();
|
case SOUTH -> dx > 0.5 ? Edge.SOUTH_EAST : Edge.SOUTH_WEST;
|
||||||
|
case WEST -> dz > 0.5 ? Edge.SOUTH_WEST : Edge.NORTH_WEST;
|
||||||
//distances your click was from the 6 faces of the block (each 0..1)
|
default -> throw new IllegalArgumentException();
|
||||||
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);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
//I may have skill issue
|
//I may have skill issue
|
||||||
@ -106,18 +86,4 @@ public enum Edge implements StringIdentifiable {
|
|||||||
public String asString() {
|
public String asString() {
|
||||||
return name().toLowerCase(Locale.ROOT);
|
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