Assisted Placement, Part I

- refactor existing placement helpers
- add placement helpers for cogs and shafts
This commit is contained in:
Zelophed 2020-12-16 17:36:22 +01:00
parent 743d303bbe
commit e950aa268f
15 changed files with 666 additions and 308 deletions

View file

@ -1,28 +1,21 @@
package com.simibubi.create.content.contraptions.components.structureMovement.bearing; package com.simibubi.create.content.contraptions.components.structureMovement.bearing;
import java.util.ArrayList;
import java.util.HashSet;
import java.util.List;
import java.util.Set;
import javax.annotation.Nullable;
import com.simibubi.create.AllBlocks; import com.simibubi.create.AllBlocks;
import com.simibubi.create.AllShapes; import com.simibubi.create.AllShapes;
import com.simibubi.create.foundation.block.ProperDirectionalBlock; import com.simibubi.create.foundation.block.ProperDirectionalBlock;
import com.simibubi.create.foundation.utility.DyeHelper; import com.simibubi.create.foundation.utility.DyeHelper;
import com.simibubi.create.foundation.utility.Iterate; import com.simibubi.create.foundation.utility.Iterate;
import com.simibubi.create.foundation.utility.VecHelper;
import com.simibubi.create.foundation.utility.placement.IPlacementHelper;
import com.simibubi.create.foundation.utility.placement.PlacementHelpers;
import com.simibubi.create.foundation.utility.placement.PlacementOffset;
import mcp.MethodsReturnNonnullByDefault;
import net.minecraft.block.Block; import net.minecraft.block.Block;
import net.minecraft.block.BlockState; import net.minecraft.block.BlockState;
import net.minecraft.entity.Entity; import net.minecraft.entity.Entity;
import net.minecraft.entity.LivingEntity; import net.minecraft.entity.LivingEntity;
import net.minecraft.entity.player.PlayerEntity; import net.minecraft.entity.player.PlayerEntity;
import net.minecraft.item.BlockItem; import net.minecraft.item.*;
import net.minecraft.item.BlockItemUseContext;
import net.minecraft.item.DyeColor;
import net.minecraft.item.ItemStack;
import net.minecraft.item.ShearsItem;
import net.minecraft.util.ActionResultType; import net.minecraft.util.ActionResultType;
import net.minecraft.util.Direction; import net.minecraft.util.Direction;
import net.minecraft.util.Hand; import net.minecraft.util.Hand;
@ -35,6 +28,13 @@ import net.minecraft.util.math.shapes.VoxelShape;
import net.minecraft.world.IBlockReader; import net.minecraft.world.IBlockReader;
import net.minecraft.world.World; import net.minecraft.world.World;
import javax.annotation.Nullable;
import java.util.ArrayList;
import java.util.HashSet;
import java.util.List;
import java.util.Set;
import java.util.function.Predicate;
public class SailBlock extends ProperDirectionalBlock { public class SailBlock extends ProperDirectionalBlock {
public static SailBlock frame(Properties properties) { public static SailBlock frame(Properties properties) {
@ -45,7 +45,9 @@ public class SailBlock extends ProperDirectionalBlock {
return new SailBlock(properties, false); return new SailBlock(properties, false);
} }
private boolean frame; private static final int placementHelperId = PlacementHelpers.register(new PlacementHelper());
private final boolean frame;
protected SailBlock(Properties p_i48415_1_, boolean frame) { protected SailBlock(Properties p_i48415_1_, boolean frame) {
super(p_i48415_1_); super(p_i48415_1_);
@ -55,27 +57,27 @@ public class SailBlock extends ProperDirectionalBlock {
@Override @Override
public BlockState getStateForPlacement(BlockItemUseContext context) { public BlockState getStateForPlacement(BlockItemUseContext context) {
BlockState state = super.getStateForPlacement(context); BlockState state = super.getStateForPlacement(context);
return state.with(FACING, state.get(FACING) return state.with(FACING, state.get(FACING).getOpposite());
.getOpposite());
} }
@Override @Override
public ActionResultType onUse(BlockState state, World world, BlockPos pos, PlayerEntity player, Hand hand, public ActionResultType onUse(BlockState state, World world, BlockPos pos, PlayerEntity player, Hand hand, BlockRayTraceResult ray) {
BlockRayTraceResult ray) {
ItemStack heldItem = player.getHeldItem(hand); ItemStack heldItem = player.getHeldItem(hand);
if (AllBlocks.SAIL.isIn(heldItem) || AllBlocks.SAIL_FRAME.isIn(heldItem)) { if (AllBlocks.SAIL.isIn(heldItem) || AllBlocks.SAIL_FRAME.isIn(heldItem)) {
Direction offset = IPlacementHelper placementHelper = PlacementHelpers.get(placementHelperId);
SailBlockPlacementHelper.getPlacementOffset(world, state.get(FACING), pos, ray.getHitVec()); PlacementOffset offset = placementHelper.getOffset(world, state, pos, ray);
if (offset == null)
return ActionResultType.SUCCESS; if (!offset.isSuccessful())
return ActionResultType.PASS;
BlockState blockState = ((BlockItem) heldItem.getItem()).getBlock() BlockState blockState = ((BlockItem) heldItem.getItem()).getBlock()
.getDefaultState() .getDefaultState()
.with(FACING, state.get(FACING)); .with(FACING, state.get(FACING));
BlockPos offsetPos = pos.offset(offset); BlockPos offsetPos = new BlockPos(offset.getPos());
if (!world.isRemote && world.getBlockState(offsetPos) if (!world.isRemote && world.getBlockState(offsetPos)
.getMaterial() .getMaterial()
.isReplaceable()) { .isReplaceable()) {
world.setBlockState(offsetPos, blockState); world.setBlockState(offsetPos, blockState);
if (!player.isCreative()) if (!player.isCreative())
heldItem.shrink(1); heldItem.shrink(1);
@ -94,7 +96,7 @@ public class SailBlock extends ProperDirectionalBlock {
for (DyeColor color : DyeColor.values()) { for (DyeColor color : DyeColor.values()) {
if (!heldItem.getItem() if (!heldItem.getItem()
.isIn(DyeHelper.getTagOfDye(color))) .isIn(DyeHelper.getTagOfDye(color)))
continue; continue;
if (!world.isRemote) if (!world.isRemote)
applyDye(state, world, pos, color); applyDye(state, world, pos, color);
@ -106,8 +108,8 @@ public class SailBlock extends ProperDirectionalBlock {
protected void applyDye(BlockState state, World world, BlockPos pos, @Nullable DyeColor color) { protected void applyDye(BlockState state, World world, BlockPos pos, @Nullable DyeColor color) {
BlockState newState = BlockState newState =
(color == null ? AllBlocks.SAIL_FRAME : AllBlocks.DYED_SAILS[color.ordinal()]).getDefaultState() (color == null ? AllBlocks.SAIL_FRAME : AllBlocks.DYED_SAILS[color.ordinal()]).getDefaultState()
.with(FACING, state.get(FACING)); .with(FACING, state.get(FACING));
// Dye the block itself // Dye the block itself
if (state != newState) { if (state != newState) {
@ -118,7 +120,7 @@ public class SailBlock extends ProperDirectionalBlock {
// Dye all adjacent // Dye all adjacent
for (Direction d : Iterate.directions) { for (Direction d : Iterate.directions) {
if (d.getAxis() == state.get(FACING) if (d.getAxis() == state.get(FACING)
.getAxis()) .getAxis())
continue; continue;
BlockPos offset = pos.offset(d); BlockPos offset = pos.offset(d);
BlockState adjacentState = world.getBlockState(offset); BlockState adjacentState = world.getBlockState(offset);
@ -145,7 +147,7 @@ public class SailBlock extends ProperDirectionalBlock {
for (Direction d : Iterate.directions) { for (Direction d : Iterate.directions) {
if (d.getAxis() == state.get(FACING) if (d.getAxis() == state.get(FACING)
.getAxis()) .getAxis())
continue; continue;
BlockPos offset = currentPos.offset(d); BlockPos offset = currentPos.offset(d);
if (visited.contains(offset)) if (visited.contains(offset))
@ -163,26 +165,23 @@ public class SailBlock extends ProperDirectionalBlock {
} }
@Override @Override
public VoxelShape getShape(BlockState state, IBlockReader p_220053_2_, BlockPos p_220053_3_, public VoxelShape getShape(BlockState state, IBlockReader p_220053_2_, BlockPos p_220053_3_, ISelectionContext p_220053_4_) {
ISelectionContext p_220053_4_) {
return (frame ? AllShapes.SAIL_FRAME : AllShapes.SAIL).get(state.get(FACING)); return (frame ? AllShapes.SAIL_FRAME : AllShapes.SAIL).get(state.get(FACING));
} }
@Override @Override
public VoxelShape getCollisionShape(BlockState state, IBlockReader p_220071_2_, BlockPos p_220071_3_, public VoxelShape getCollisionShape(BlockState state, IBlockReader p_220071_2_, BlockPos p_220071_3_, ISelectionContext p_220071_4_) {
ISelectionContext p_220071_4_) {
if (frame) if (frame)
return AllShapes.SAIL_FRAME_COLLISION.get(state.get(FACING)); return AllShapes.SAIL_FRAME_COLLISION.get(state.get(FACING));
return getShape(state, p_220071_2_, p_220071_3_, p_220071_4_); return getShape(state, p_220071_2_, p_220071_3_, p_220071_4_);
} }
@Override @Override
public ItemStack getPickBlock(BlockState state, RayTraceResult target, IBlockReader world, BlockPos pos, public ItemStack getPickBlock(BlockState state, RayTraceResult target, IBlockReader world, BlockPos pos, PlayerEntity player) {
PlayerEntity player) {
ItemStack pickBlock = super.getPickBlock(state, target, world, pos, player); ItemStack pickBlock = super.getPickBlock(state, target, world, pos, player);
if (pickBlock.isEmpty()) if (pickBlock.isEmpty())
return AllBlocks.SAIL.get() return AllBlocks.SAIL.get()
.getPickBlock(state, target, world, pos, player); .getPickBlock(state, target, world, pos, player);
return pickBlock; return pickBlock;
} }
@ -209,4 +208,32 @@ public class SailBlock extends ProperDirectionalBlock {
} }
@MethodsReturnNonnullByDefault
private static class PlacementHelper implements IPlacementHelper {
@Override
public Predicate<ItemStack> getItemPredicate() {
return i -> AllBlocks.SAIL.isIn(i) || AllBlocks.SAIL_FRAME.isIn(i);
}
@Override
public Predicate<BlockState> getStatePredicate() {
return s -> s.getBlock() instanceof SailBlock;
}
@Override
public PlacementOffset getOffset(World world, BlockState state, BlockPos pos, BlockRayTraceResult ray) {
List<Direction> directions = IPlacementHelper.orderedByDistanceExceptAxis(pos, ray.getHitVec(), state.get(SailBlock.FACING).getAxis(), dir -> world.getBlockState(pos.offset(dir)).getMaterial().isReplaceable());
if (directions.isEmpty())
return PlacementOffset.fail();
else {
return PlacementOffset.success(pos.offset(directions.get(0)));
}
}
@Override
public void renderAt(BlockPos pos, BlockState state, BlockRayTraceResult ray, PlacementOffset offset) {
IPlacementHelper.renderArrow(VecHelper.getCenterOf(pos), VecHelper.getCenterOf(offset.getPos()), state.get(FACING));
}
}
} }

View file

@ -1,101 +0,0 @@
package com.simibubi.create.content.contraptions.components.structureMovement.bearing;
import com.simibubi.create.AllBlocks;
import com.simibubi.create.CreateClient;
import com.simibubi.create.foundation.utility.Iterate;
import com.simibubi.create.foundation.utility.VecHelper;
import net.minecraft.block.BlockState;
import net.minecraft.client.Minecraft;
import net.minecraft.client.entity.player.ClientPlayerEntity;
import net.minecraft.client.world.ClientWorld;
import net.minecraft.entity.player.PlayerEntity;
import net.minecraft.item.ItemStack;
import net.minecraft.util.Direction;
import net.minecraft.util.Hand;
import net.minecraft.util.math.BlockPos;
import net.minecraft.util.math.BlockRayTraceResult;
import net.minecraft.util.math.RayTraceResult;
import net.minecraft.util.math.Vec3d;
import net.minecraft.world.World;
import net.minecraftforge.api.distmarker.Dist;
import net.minecraftforge.api.distmarker.OnlyIn;
public class SailBlockPlacementHelper {
@OnlyIn(Dist.CLIENT)
public static void tick() {
Minecraft mc = Minecraft.getInstance();
RayTraceResult objectMouseOver = mc.objectMouseOver;
ClientWorld world = mc.world;
ClientPlayerEntity player = mc.player;
if (!(objectMouseOver instanceof BlockRayTraceResult))
return;
BlockRayTraceResult ray = (BlockRayTraceResult) objectMouseOver;
if (!isHoldingSail(player))
return;
BlockPos pos = ray.getPos();
BlockState blockState = world.getBlockState(pos);
if (!(blockState.getBlock() instanceof SailBlock))
return;
Direction sailFacing = blockState.get(SailBlock.FACING);
Direction offset = getPlacementOffset(world, sailFacing, pos, ray.getHitVec());
if (offset == null)
return;
Vec3d centerOf = VecHelper.getCenterOf(pos);
Vec3d offsetVec = new Vec3d(offset.getDirectionVec());
if (!world.getBlockState(pos.offset(offset))
.getMaterial()
.isReplaceable())
return;
for (Direction caretDirection : Iterate.directions) {
if (caretDirection.getAxis() == offset.getAxis())
continue;
if (caretDirection.getAxis() == sailFacing.getAxis())
continue;
Vec3d otherOffset = new Vec3d(caretDirection.getDirectionVec()).scale(.25f);
Vec3d start = offsetVec.scale(.75f)
.add(otherOffset);
Vec3d target = centerOf.add(offsetVec);
CreateClient.outliner.showLine("sailHelp" + caretDirection, centerOf.add(start), target)
.lineWidth(1 / 16f);
}
return;
}
public static boolean isHoldingSail(PlayerEntity player) {
for (Hand hand : Hand.values()) {
ItemStack heldItem = player.getHeldItem(hand);
if (AllBlocks.SAIL.isIn(heldItem) || AllBlocks.SAIL_FRAME.isIn(heldItem))
return true;
}
return false;
}
public static Direction getPlacementOffset(World world, Direction sailDirection, BlockPos pos, Vec3d hit) {
Direction argMin = null;
float min = Float.MAX_VALUE;
Vec3d diffFromCentre = hit.subtract(VecHelper.getCenterOf(pos));
for (Direction side : Iterate.directions) {
if (side.getAxis() == sailDirection.getAxis())
continue;
if (!world.getBlockState(pos.offset(side))
.getMaterial()
.isReplaceable())
continue;
float distance = (float) new Vec3d(side.getDirectionVec()).distanceTo(diffFromCentre);
if (distance > min)
continue;
min = distance;
argMin = side;
}
return argMin;
}
}

View file

@ -77,7 +77,7 @@ public class PistonContraption extends TranslatingContraption {
return false; return false;
if (blockState.get(MechanicalPistonBlock.STATE) == PistonState.EXTENDED) { if (blockState.get(MechanicalPistonBlock.STATE) == PistonState.EXTENDED) {
while (PistonPolePlacementHelper.matchesAxis(nextBlock, direction.getAxis()) || isPistonHead(nextBlock) && nextBlock.get(FACING) == direction) { while (PistonExtensionPoleBlock.PlacementHelper.get().matchesAxis(nextBlock, direction.getAxis()) || isPistonHead(nextBlock) && nextBlock.get(FACING) == direction) {
actualStart = actualStart.offset(direction); actualStart = actualStart.offset(direction);
poles.add(new BlockInfo(actualStart, nextBlock.with(FACING, direction), null)); poles.add(new BlockInfo(actualStart, nextBlock.with(FACING, direction), null));
@ -104,7 +104,7 @@ public class PistonContraption extends TranslatingContraption {
nextBlock = world.getBlockState(end.offset(direction.getOpposite())); nextBlock = world.getBlockState(end.offset(direction.getOpposite()));
int extensionsInBack = 0; int extensionsInBack = 0;
while (PistonPolePlacementHelper.matchesAxis(nextBlock, direction.getAxis())) { while (PistonExtensionPoleBlock.PlacementHelper.get().matchesAxis(nextBlock, direction.getAxis())) {
end = end.offset(direction.getOpposite()); end = end.offset(direction.getOpposite());
poles.add(new BlockInfo(end, nextBlock.with(FACING, direction), null)); poles.add(new BlockInfo(end, nextBlock.with(FACING, direction), null));
extensionsInBack++; extensionsInBack++;

View file

@ -5,7 +5,11 @@ import com.simibubi.create.AllShapes;
import com.simibubi.create.content.contraptions.components.structureMovement.piston.MechanicalPistonBlock.*; import com.simibubi.create.content.contraptions.components.structureMovement.piston.MechanicalPistonBlock.*;
import com.simibubi.create.content.contraptions.wrench.IWrenchable; import com.simibubi.create.content.contraptions.wrench.IWrenchable;
import com.simibubi.create.foundation.block.ProperDirectionalBlock; import com.simibubi.create.foundation.block.ProperDirectionalBlock;
import com.simibubi.create.foundation.utility.Pair; import com.simibubi.create.foundation.utility.placement.IPlacementHelper;
import com.simibubi.create.foundation.utility.placement.PlacementHelpers;
import com.simibubi.create.foundation.utility.placement.PlacementOffset;
import com.simibubi.create.foundation.utility.placement.util.PoleHelper;
import mcp.MethodsReturnNonnullByDefault;
import net.minecraft.block.Block; import net.minecraft.block.Block;
import net.minecraft.block.BlockState; import net.minecraft.block.BlockState;
import net.minecraft.block.IWaterLoggable; import net.minecraft.block.IWaterLoggable;
@ -30,10 +34,14 @@ import net.minecraft.world.IBlockReader;
import net.minecraft.world.IWorld; import net.minecraft.world.IWorld;
import net.minecraft.world.World; import net.minecraft.world.World;
import java.util.function.Predicate;
import static com.simibubi.create.content.contraptions.components.structureMovement.piston.MechanicalPistonBlock.*; import static com.simibubi.create.content.contraptions.components.structureMovement.piston.MechanicalPistonBlock.*;
public class PistonExtensionPoleBlock extends ProperDirectionalBlock implements IWrenchable, IWaterLoggable { public class PistonExtensionPoleBlock extends ProperDirectionalBlock implements IWrenchable, IWaterLoggable {
private static final int placementHelperId = PlacementHelpers.register(PlacementHelper.get());
public PistonExtensionPoleBlock(Properties properties) { public PistonExtensionPoleBlock(Properties properties) {
super(properties); super(properties);
setDefaultState(getDefaultState().with(FACING, Direction.UP).with(BlockStateProperties.WATERLOGGED, false)); setDefaultState(getDefaultState().with(FACING, Direction.UP).with(BlockStateProperties.WATERLOGGED, false));
@ -107,12 +115,13 @@ public class PistonExtensionPoleBlock extends ProperDirectionalBlock implements
ItemStack heldItem = player.getHeldItem(hand); ItemStack heldItem = player.getHeldItem(hand);
if (AllBlocks.PISTON_EXTENSION_POLE.isIn(heldItem) && !player.isSneaking()) { if (AllBlocks.PISTON_EXTENSION_POLE.isIn(heldItem) && !player.isSneaking()) {
Pair<Direction, Integer> offset = PistonPolePlacementHelper.getPlacementOffset(world, state.get(FACING).getAxis(), pos, ray.getHitVec()); IPlacementHelper placementHelper = PlacementHelpers.get(placementHelperId);
PlacementOffset offset = placementHelper.getOffset(world, state, pos, ray);
if (offset == null || offset.getSecond() == 0) if (!offset.isSuccessful())
return ActionResultType.PASS; return ActionResultType.PASS;
BlockPos newPos = pos.offset(offset.getFirst(), offset.getSecond()); BlockPos newPos = new BlockPos(offset.getPos());
if (!world.getBlockState(newPos).getMaterial().isReplaceable()) if (!world.getBlockState(newPos).getMaterial().isReplaceable())
return ActionResultType.PASS; return ActionResultType.PASS;
@ -120,7 +129,7 @@ public class PistonExtensionPoleBlock extends ProperDirectionalBlock implements
if (world.isRemote) if (world.isRemote)
return ActionResultType.SUCCESS; return ActionResultType.SUCCESS;
world.setBlockState(newPos, AllBlocks.PISTON_EXTENSION_POLE.getDefaultState().with(FACING, state.get(FACING))); world.setBlockState(newPos, offset.getTransform().apply(AllBlocks.PISTON_EXTENSION_POLE.getDefaultState()));
if (!player.isCreative()) if (!player.isCreative())
heldItem.shrink(1); heldItem.shrink(1);
@ -149,4 +158,27 @@ public class PistonExtensionPoleBlock extends ProperDirectionalBlock implements
} }
return state; return state;
} }
@MethodsReturnNonnullByDefault
public static class PlacementHelper extends PoleHelper<Direction> {
private static final PlacementHelper instance = new PlacementHelper();
public static PlacementHelper get() {
return instance;
}
private PlacementHelper(){
super(
AllBlocks.PISTON_EXTENSION_POLE::has,
state -> state.get(FACING).getAxis(),
FACING
);
}
@Override
public Predicate<ItemStack> getItemPredicate() {
return AllBlocks.PISTON_EXTENSION_POLE::isIn;
}
}
} }

View file

@ -1,129 +0,0 @@
package com.simibubi.create.content.contraptions.components.structureMovement.piston;
import com.simibubi.create.AllBlocks;
import com.simibubi.create.CreateClient;
import com.simibubi.create.foundation.utility.Iterate;
import com.simibubi.create.foundation.utility.Pair;
import com.simibubi.create.foundation.utility.VecHelper;
import net.minecraft.block.BlockState;
import net.minecraft.client.Minecraft;
import net.minecraft.client.world.ClientWorld;
import net.minecraft.entity.player.PlayerEntity;
import net.minecraft.util.Direction;
import net.minecraft.util.Hand;
import net.minecraft.util.math.BlockPos;
import net.minecraft.util.math.BlockRayTraceResult;
import net.minecraft.util.math.Vec3d;
import net.minecraft.world.World;
import net.minecraftforge.api.distmarker.Dist;
import net.minecraftforge.api.distmarker.OnlyIn;
import java.util.Arrays;
public class PistonPolePlacementHelper {
@OnlyIn(Dist.CLIENT)
public static void tick() {
Minecraft mc = Minecraft.getInstance();
ClientWorld world = mc.world;
if (!(mc.objectMouseOver instanceof BlockRayTraceResult))
return;
BlockRayTraceResult ray = (BlockRayTraceResult) mc.objectMouseOver;
if (mc.player != null && !isHoldingPole(mc.player))
return;
if (mc.player.isSneaking())
return;
BlockPos pos = ray.getPos();
BlockState state = world.getBlockState(pos);
if (!(state.getBlock() instanceof PistonExtensionPoleBlock))
return;
Pair<Direction, Integer> offset = getPlacementOffset(world, state.get(PistonExtensionPoleBlock.FACING).getAxis(), pos, ray.getHitVec());
if (offset == null || offset.getSecond() == 0)
return;
Direction hitFace = ray.getFace();
if (hitFace.getAxis() == offset.getFirst().getAxis())
return;
Vec3d hitCenter = VecHelper.getCenterOf(pos).add(new Vec3d(hitFace.getDirectionVec()).scale(0.3));
//get the two perpendicular directions to form the arrow
Direction[] directions = Arrays.stream(Direction.Axis.values()).filter(axis -> axis != hitFace.getAxis() && axis != offset.getFirst().getAxis()).map(Iterate::directionsInAxis).findFirst().orElse(new Direction[]{});
Vec3d startOffset = new Vec3d(offset.getFirst().getDirectionVec());
Vec3d start = hitCenter.add(startOffset);
for (Direction dir : directions) {
Vec3d arrowOffset = new Vec3d(dir.getDirectionVec()).scale(.25);
Vec3d target = hitCenter.add(startOffset.scale(0.75)).add(arrowOffset);
CreateClient.outliner.showLine("poleHelp" + offset.getFirst() + dir, start, target).lineWidth(1/16f);
}
}
// first indicates the direction that the position needs to be offset into
// second indicates by how many blocks the position needs to be offset by; is 0 if there was no valid position on either end of the pole
public static Pair<Direction, Integer> getPlacementOffset(World world, Direction.Axis poleAxis, BlockPos pos, Vec3d hit) {
Pair<Direction, Integer> offset = null;
double min = Double.MAX_VALUE;
Vec3d localPos = hit.subtract(VecHelper.getCenterOf(pos));
//find target direction
for (Direction dir : Iterate.directionsInAxis(poleAxis)) {
double distance = new Vec3d(dir.getDirectionVec()).distanceTo(localPos);
if (distance > min)
continue;
min = distance;
offset = Pair.of(dir, 0);
}
if (offset == null)//??
return null;
//check for space at the end of the pole
int poles = attachedPoles(world, pos, offset.getFirst());
BlockState state = world.getBlockState(pos.offset(offset.getFirst(), poles + 1));
if (state.getMaterial().isReplaceable()) {
offset.setSecond(poles + 1);
return offset;
}
//check the other end of the pole
offset.setFirst(offset.getFirst().getOpposite());
poles = attachedPoles(world, pos, offset.getFirst());
state = world.getBlockState(pos.offset(offset.getFirst(), poles + 1));
if (state.getMaterial().isReplaceable()) {
offset.setSecond(poles + 1);
}
return offset;
}
public static int attachedPoles(World world, BlockPos pos, Direction direction) {
BlockPos checkPos = pos.offset(direction);
BlockState state = world.getBlockState(checkPos);
int count = 0;
while (matchesAxis(state, direction.getAxis())) {
count++;
checkPos = checkPos.offset(direction);
state = world.getBlockState(checkPos);
}
return count;
}
//checks if the given state is a piston pole on the given axis
public static boolean matchesAxis(BlockState state, Direction.Axis axis) {
return AllBlocks.PISTON_EXTENSION_POLE.has(state) && state.get(PistonExtensionPoleBlock.FACING).getAxis() == axis;
}
public static boolean isHoldingPole(PlayerEntity player) {
return Arrays.stream(Hand.values()).anyMatch(hand -> AllBlocks.PISTON_EXTENSION_POLE.isIn(player.getHeldItem(hand)));
}
}

View file

@ -6,7 +6,6 @@ import com.simibubi.create.AllItems;
import com.simibubi.create.CreateClient; import com.simibubi.create.CreateClient;
import com.simibubi.create.content.contraptions.components.structureMovement.piston.MechanicalPistonBlock; import com.simibubi.create.content.contraptions.components.structureMovement.piston.MechanicalPistonBlock;
import com.simibubi.create.content.contraptions.components.structureMovement.piston.PistonExtensionPoleBlock; import com.simibubi.create.content.contraptions.components.structureMovement.piston.PistonExtensionPoleBlock;
import com.simibubi.create.content.contraptions.components.structureMovement.piston.PistonPolePlacementHelper;
import com.simibubi.create.foundation.config.AllConfigs; import com.simibubi.create.foundation.config.AllConfigs;
import com.simibubi.create.foundation.gui.GuiGameElement; import com.simibubi.create.foundation.gui.GuiGameElement;
import com.simibubi.create.foundation.tileEntity.behaviour.ValueBox; import com.simibubi.create.foundation.tileEntity.behaviour.ValueBox;
@ -14,7 +13,6 @@ import com.simibubi.create.foundation.utility.Iterate;
import com.simibubi.create.foundation.utility.Lang; import com.simibubi.create.foundation.utility.Lang;
import com.simibubi.create.foundation.utility.outliner.Outline; import com.simibubi.create.foundation.utility.outliner.Outline;
import com.simibubi.create.foundation.utility.outliner.Outliner.OutlineEntry; import com.simibubi.create.foundation.utility.outliner.Outliner.OutlineEntry;
import net.minecraft.block.BlockState; import net.minecraft.block.BlockState;
import net.minecraft.client.Minecraft; import net.minecraft.client.Minecraft;
import net.minecraft.client.gui.screen.Screen; import net.minecraft.client.gui.screen.Screen;
@ -106,7 +104,7 @@ public class GoggleOverlayRenderer {
int poles = 1; int poles = 1;
boolean pistonFound = false; boolean pistonFound = false;
for (Direction dir : directions) { for (Direction dir : directions) {
int attachedPoles = PistonPolePlacementHelper.attachedPoles(world, pos, dir); int attachedPoles = PistonExtensionPoleBlock.PlacementHelper.get().attachedPoles(world, pos, dir);
poles += attachedPoles; poles += attachedPoles;
pistonFound |= world.getBlockState(pos.offset(dir, attachedPoles + 1)) pistonFound |= world.getBlockState(pos.offset(dir, attachedPoles + 1))
.getBlock() instanceof MechanicalPistonBlock; .getBlock() instanceof MechanicalPistonBlock;

View file

@ -5,7 +5,6 @@ import com.simibubi.create.AllShapes;
import com.simibubi.create.content.contraptions.base.IRotate; import com.simibubi.create.content.contraptions.base.IRotate;
import com.simibubi.create.content.contraptions.relays.advanced.SpeedControllerBlock; import com.simibubi.create.content.contraptions.relays.advanced.SpeedControllerBlock;
import com.simibubi.create.foundation.utility.Iterate; import com.simibubi.create.foundation.utility.Iterate;
import net.minecraft.block.Block; import net.minecraft.block.Block;
import net.minecraft.block.BlockState; import net.minecraft.block.BlockState;
import net.minecraft.fluid.Fluids; import net.minecraft.fluid.Fluids;
@ -69,9 +68,7 @@ public class CogWheelBlock extends AbstractShaftBlock {
@Override @Override
public BlockState getStateForPlacement(BlockItemUseContext context) { public BlockState getStateForPlacement(BlockItemUseContext context) {
BlockPos placedOnPos = context.getPos() BlockPos placedOnPos = context.getPos().offset(context.getFace().getOpposite());
.offset(context.getFace()
.getOpposite());
World world = context.getWorld(); World world = context.getWorld();
BlockState placedAgainst = world.getBlockState(placedOnPos); BlockState placedAgainst = world.getBlockState(placedOnPos);
Block block = placedAgainst.getBlock(); Block block = placedAgainst.getBlock();
@ -80,7 +77,8 @@ public class CogWheelBlock extends AbstractShaftBlock {
.down()); .down());
IFluidState ifluidstate = context.getWorld().getFluidState(context.getPos()); IFluidState ifluidstate = context.getWorld().getFluidState(context.getPos());
if (AllBlocks.ROTATION_SPEED_CONTROLLER.has(stateBelow) && isLarge) { if (AllBlocks.ROTATION_SPEED_CONTROLLER.has(stateBelow) && isLarge) {
return this.getDefaultState().with(BlockStateProperties.WATERLOGGED, Boolean.valueOf(ifluidstate.getFluid() == Fluids.WATER)) return this.getDefaultState()
.with(BlockStateProperties.WATERLOGGED, ifluidstate.getFluid() == Fluids.WATER)
.with(AXIS, stateBelow.get(SpeedControllerBlock.HORIZONTAL_AXIS) == Axis.X ? Axis.Z : Axis.X); .with(AXIS, stateBelow.get(SpeedControllerBlock.HORIZONTAL_AXIS) == Axis.X ? Axis.Z : Axis.X);
} }
@ -89,10 +87,11 @@ public class CogWheelBlock extends AbstractShaftBlock {
Axis preferredAxis = getPreferredAxis(context); Axis preferredAxis = getPreferredAxis(context);
if (preferredAxis != null) if (preferredAxis != null)
return this.getDefaultState() return this.getDefaultState()
.with(AXIS, preferredAxis).with(BlockStateProperties.WATERLOGGED, Boolean.valueOf(ifluidstate.getFluid() == Fluids.WATER)); .with(AXIS, preferredAxis)
.with(BlockStateProperties.WATERLOGGED, ifluidstate.getFluid() == Fluids.WATER);
return this.getDefaultState() return this.getDefaultState()
.with(AXIS, context.getFace() .with(AXIS, context.getFace().getAxis())
.getAxis()).with(BlockStateProperties.WATERLOGGED, Boolean.valueOf(ifluidstate.getFluid() == Fluids.WATER)); .with(BlockStateProperties.WATERLOGGED, ifluidstate.getFluid() == Fluids.WATER);
} }
return getDefaultState().with(AXIS, ((IRotate) block).getRotationAxis(placedAgainst)); return getDefaultState().with(AXIS, ((IRotate) block).getRotationAxis(placedAgainst));

View file

@ -1,38 +1,76 @@
package com.simibubi.create.content.contraptions.relays.elementary; package com.simibubi.create.content.contraptions.relays.elementary;
import com.simibubi.create.AllBlocks; import com.simibubi.create.AllBlocks;
import com.simibubi.create.AllShapes;
import com.simibubi.create.foundation.advancement.AllTriggers; import com.simibubi.create.foundation.advancement.AllTriggers;
import com.simibubi.create.foundation.utility.Iterate; import com.simibubi.create.foundation.utility.Iterate;
import com.simibubi.create.foundation.utility.VecHelper; import com.simibubi.create.foundation.utility.VecHelper;
import com.simibubi.create.foundation.utility.placement.IPlacementHelper;
import com.simibubi.create.foundation.utility.placement.PlacementHelpers;
import com.simibubi.create.foundation.utility.placement.PlacementOffset;
import mcp.MethodsReturnNonnullByDefault;
import net.minecraft.block.BlockState; import net.minecraft.block.BlockState;
import net.minecraft.entity.player.PlayerEntity; import net.minecraft.entity.player.PlayerEntity;
import net.minecraft.item.BlockItem; import net.minecraft.item.BlockItem;
import net.minecraft.item.BlockItemUseContext; import net.minecraft.item.BlockItemUseContext;
import net.minecraft.item.ItemStack;
import net.minecraft.util.ActionResultType; import net.minecraft.util.ActionResultType;
import net.minecraft.util.Direction; import net.minecraft.util.Direction;
import net.minecraft.util.Direction.Axis; import net.minecraft.util.Direction.Axis;
import net.minecraft.util.Direction.AxisDirection; import net.minecraft.util.Direction.AxisDirection;
import net.minecraft.util.math.BlockPos; import net.minecraft.util.math.BlockPos;
import net.minecraft.util.math.Vec3d; import net.minecraft.util.math.BlockRayTraceResult;
import net.minecraft.world.World; import net.minecraft.world.World;
import java.util.List;
import java.util.function.Predicate;
import static com.simibubi.create.content.contraptions.relays.elementary.CogWheelBlock.AXIS;
public class CogwheelBlockItem extends BlockItem { public class CogwheelBlockItem extends BlockItem {
boolean large; boolean large;
private final int placementHelperId;
public CogwheelBlockItem(CogWheelBlock block, Properties builder) { public CogwheelBlockItem(CogWheelBlock block, Properties builder) {
super(block, builder); super(block, builder);
large = block.isLarge; large = block.isLarge;
placementHelperId = PlacementHelpers.register(large ? new LargeCogHelper() : new SmallCogHelper());
} }
@Override @Override
public ActionResultType tryPlace(BlockItemUseContext context) { public ActionResultType tryPlace(BlockItemUseContext context) {
Direction face = context.getFace(); IPlacementHelper helper = PlacementHelpers.get(placementHelperId);
BlockPos placedOnPos = context.getPos().offset(face.getOpposite()); World world = context.getWorld();
BlockState placedOnState = context.getWorld().getBlockState(placedOnPos); BlockPos pos = context.getPos().offset(context.getFace().getOpposite());
BlockState state = world.getBlockState(pos);
if (!(placedOnState.getBlock() instanceof CogWheelBlock)) if (!helper.getStatePredicate().test(state))
return super.tryPlace(context);
PlacementOffset offset = helper.getOffset(world, state, pos, new BlockRayTraceResult(context.getHitVec(), context.getFace(), pos, true));
if (!offset.isSuccessful())
return super.tryPlace(context);
BlockPos newPos = new BlockPos(offset.getPos());
if (!world.getBlockState(newPos).getMaterial().isReplaceable())
return ActionResultType.PASS;
if (world.isRemote)
return ActionResultType.SUCCESS;
world.setBlockState(newPos, offset.getTransform().apply(this.getBlock().getDefaultState()));
if (context.getPlayer() != null && !context.getPlayer().isCreative()) {
context.getItem().shrink(1);
}
return ActionResultType.SUCCESS;
/*if (!(placedOnState.getBlock() instanceof CogWheelBlock))
return super.tryPlace(context); return super.tryPlace(context);
if (face.getAxis() == placedOnState.get(CogWheelBlock.AXIS)) if (face.getAxis() == placedOnState.get(CogWheelBlock.AXIS))
return super.tryPlace(context); return super.tryPlace(context);
@ -67,7 +105,7 @@ public class CogwheelBlockItem extends BlockItem {
return ActionResultType.FAIL; return ActionResultType.FAIL;
} }
return super.tryPlace(context); return super.tryPlace(context);*/
} }
@Override @Override
@ -107,4 +145,137 @@ public class CogwheelBlockItem extends BlockItem {
return super.placeBlock(context, state); return super.placeBlock(context, state);
} }
@MethodsReturnNonnullByDefault
private static class SmallCogHelper extends DiagonalCogHelper {
@Override
public Predicate<ItemStack> getItemPredicate() {
return AllBlocks.COGWHEEL::isIn;
}
@Override
public PlacementOffset getOffset(World world, BlockState state, BlockPos pos, BlockRayTraceResult ray) {
if (hitOnShaft(state, ray))
return PlacementOffset.fail();
if (!((CogWheelBlock) state.getBlock()).isLarge) {
List<Direction> directions = IPlacementHelper.orderedByDistanceExceptAxis(pos, ray.getHitVec(), state.get(AXIS));
for (Direction dir : directions) {
BlockPos newPos = pos.offset(dir);
if (hasLargeCogwheelNeighbor(world, newPos, state.get(AXIS)))
continue;
if (!world.getBlockState(newPos).getMaterial().isReplaceable())
continue;
return PlacementOffset.success(newPos, s -> s.with(AXIS, state.get(AXIS)));
}
return PlacementOffset.fail();
}
return super.getOffset(world, state, pos, ray);
}
@Override
public void renderAt(BlockPos pos, BlockState state, BlockRayTraceResult ray, PlacementOffset offset) {
IPlacementHelper.renderArrow(VecHelper.getCenterOf(pos), VecHelper.getCenterOf(offset.getPos()), Direction.getFacingFromAxis(Direction.AxisDirection.POSITIVE, state.get(AXIS)), ((CogWheelBlock) state.getBlock()).isLarge ? 1.5D : 0.75D);
}
}
@MethodsReturnNonnullByDefault
private static class LargeCogHelper extends DiagonalCogHelper {
@Override
public Predicate<ItemStack> getItemPredicate() {
return AllBlocks.LARGE_COGWHEEL::isIn;
}
@Override
public PlacementOffset getOffset(World world, BlockState state, BlockPos pos, BlockRayTraceResult ray) {
if (hitOnShaft(state, ray))
return PlacementOffset.fail();
if (((CogWheelBlock) state.getBlock()).isLarge) {
Direction side = IPlacementHelper.orderedByDistanceOnlyAxis(pos, ray.getHitVec(), state.get(AXIS)).get(0);
List<Direction> directions = IPlacementHelper.orderedByDistanceExceptAxis(pos, ray.getHitVec(), state.get(AXIS));
for (Direction dir : directions) {
BlockPos newPos = pos.offset(dir).offset(side);
if (!world.getBlockState(newPos).getMaterial().isReplaceable())
continue;
return PlacementOffset.success(newPos, s -> s.with(AXIS, dir.getAxis()));
}
return PlacementOffset.fail();
}
return super.getOffset(world, state, pos, ray);
}
}
@MethodsReturnNonnullByDefault
private abstract static class DiagonalCogHelper implements IPlacementHelper {
@Override
public Predicate<BlockState> getStatePredicate() {
return s -> s.getBlock() instanceof CogWheelBlock;
}
@Override
public PlacementOffset getOffset(World world, BlockState state, BlockPos pos, BlockRayTraceResult ray) {
//diagonal gears of different size
Direction closest = IPlacementHelper.orderedByDistanceExceptAxis(pos, ray.getHitVec(), state.get(AXIS)).get(0);
List<Direction> directions = IPlacementHelper.orderedByDistanceExceptAxis(pos, ray.getHitVec(), state.get(AXIS), d -> d.getAxis() != closest.getAxis());
for (Direction dir : directions) {
BlockPos newPos = pos.offset(dir).offset(closest);
if (!world.getBlockState(newPos).getMaterial().isReplaceable())
continue;
if (AllBlocks.COGWHEEL.has(state) && hasSmallCogwheelNeighbor(world, newPos, state.get(AXIS)))
continue;
return PlacementOffset.success(newPos, s -> s.with(AXIS, state.get(AXIS)));
}
return PlacementOffset.fail();
}
@Override
public void renderAt(BlockPos pos, BlockState state, BlockRayTraceResult ray, PlacementOffset offset) {
IPlacementHelper.renderArrow(VecHelper.getCenterOf(pos), VecHelper.getCenterOf(offset.getPos()), Direction.getFacingFromAxis(Direction.AxisDirection.POSITIVE, state.get(AXIS)), 1D);
}
protected boolean hitOnShaft(BlockState state, BlockRayTraceResult ray) {
return AllShapes.SIX_VOXEL_POLE.get(state.get(AXIS)).getBoundingBox().grow(0.001).contains(ray.getHitVec().subtract(ray.getHitVec().align(Iterate.axisSet)));
}
protected boolean hasLargeCogwheelNeighbor(World world, BlockPos pos, Direction.Axis axis) {
for (Direction dir : Iterate.directions) {
if (dir.getAxis() == axis)
continue;
if (AllBlocks.LARGE_COGWHEEL.has(world.getBlockState(pos.offset(dir))))
return true;
}
return false;
}
protected boolean hasSmallCogwheelNeighbor(World world, BlockPos pos, Direction.Axis axis) {
for (Direction dir : Iterate.directions) {
if (dir.getAxis() == axis)
continue;
if (AllBlocks.COGWHEEL.has(world.getBlockState(pos.offset(dir))))
return true;
}
return false;
}
}
} }

View file

@ -5,11 +5,18 @@ import com.simibubi.create.AllShapes;
import com.simibubi.create.content.contraptions.base.KineticTileEntity; import com.simibubi.create.content.contraptions.base.KineticTileEntity;
import com.simibubi.create.content.contraptions.relays.encased.EncasedShaftBlock; import com.simibubi.create.content.contraptions.relays.encased.EncasedShaftBlock;
import com.simibubi.create.foundation.advancement.AllTriggers; import com.simibubi.create.foundation.advancement.AllTriggers;
import com.simibubi.create.foundation.utility.placement.IPlacementHelper;
import com.simibubi.create.foundation.utility.placement.PlacementHelpers;
import com.simibubi.create.foundation.utility.placement.PlacementOffset;
import com.simibubi.create.foundation.utility.placement.util.PoleHelper;
import mcp.MethodsReturnNonnullByDefault;
import net.minecraft.block.Block;
import net.minecraft.block.BlockState; import net.minecraft.block.BlockState;
import net.minecraft.entity.player.PlayerEntity; import net.minecraft.entity.player.PlayerEntity;
import net.minecraft.item.BlockItem;
import net.minecraft.item.ItemStack; import net.minecraft.item.ItemStack;
import net.minecraft.util.ActionResultType; import net.minecraft.util.ActionResultType;
import net.minecraft.util.Direction;
import net.minecraft.util.Hand; import net.minecraft.util.Hand;
import net.minecraft.util.math.BlockPos; import net.minecraft.util.math.BlockPos;
import net.minecraft.util.math.BlockRayTraceResult; import net.minecraft.util.math.BlockRayTraceResult;
@ -18,8 +25,12 @@ import net.minecraft.util.math.shapes.VoxelShape;
import net.minecraft.world.IBlockReader; import net.minecraft.world.IBlockReader;
import net.minecraft.world.World; import net.minecraft.world.World;
import java.util.function.Predicate;
public class ShaftBlock extends AbstractShaftBlock { public class ShaftBlock extends AbstractShaftBlock {
private static final int placementHelperId = PlacementHelpers.register(new PlacementHelper());
public ShaftBlock(Properties properties) { public ShaftBlock(Properties properties) {
super(properties); super(properties);
} }
@ -45,7 +56,7 @@ public class ShaftBlock extends AbstractShaftBlock {
@Override @Override
public ActionResultType onUse(BlockState state, World world, BlockPos pos, PlayerEntity player, Hand hand, public ActionResultType onUse(BlockState state, World world, BlockPos pos, PlayerEntity player, Hand hand,
BlockRayTraceResult p_225533_6_) { BlockRayTraceResult ray) {
if (player.isSneaking() || !player.isAllowEdit()) if (player.isSneaking() || !player.isAllowEdit())
return ActionResultType.PASS; return ActionResultType.PASS;
@ -66,6 +77,52 @@ public class ShaftBlock extends AbstractShaftBlock {
return ActionResultType.SUCCESS; return ActionResultType.SUCCESS;
} }
IPlacementHelper helper = PlacementHelpers.get(placementHelperId);
if (helper.getItemPredicate().test(heldItem)) {
PlacementOffset offset = helper.getOffset(world, state, pos, ray);
if (!offset.isSuccessful())
return ActionResultType.PASS;
BlockPos newPos = new BlockPos(offset.getPos());
if (!world.getBlockState(newPos).getMaterial().isReplaceable())
return ActionResultType.PASS;
if (world.isRemote)
return ActionResultType.SUCCESS;
Block block = ((BlockItem) heldItem.getItem()).getBlock();
world.setBlockState(newPos, offset.getTransform().apply(block.getDefaultState()));
if (!player.isCreative())
heldItem.shrink(1);
return ActionResultType.SUCCESS;
}
return ActionResultType.PASS; return ActionResultType.PASS;
} }
@MethodsReturnNonnullByDefault
private static class PlacementHelper extends PoleHelper<Direction.Axis> {
//used for extending a shaft in its axis, like the piston poles. works with shafts and cogs
private PlacementHelper(){
super(
state -> state.getBlock() instanceof AbstractShaftBlock,
state -> state.get(AXIS),
AXIS
);
}
@Override
public Predicate<ItemStack> getItemPredicate() {
return i -> i.getItem() instanceof BlockItem && ((BlockItem) i.getItem()).getBlock() instanceof AbstractShaftBlock;
}
@Override
public Predicate<BlockState> getStatePredicate() {
return AllBlocks.SHAFT::has;
}
}
} }

View file

@ -1,17 +1,12 @@
package com.simibubi.create.events; package com.simibubi.create.events;
import java.util.ArrayList;
import java.util.List;
import com.mojang.blaze3d.matrix.MatrixStack; import com.mojang.blaze3d.matrix.MatrixStack;
import com.simibubi.create.AllFluids; import com.simibubi.create.AllFluids;
import com.simibubi.create.Create; import com.simibubi.create.Create;
import com.simibubi.create.CreateClient; import com.simibubi.create.CreateClient;
import com.simibubi.create.content.contraptions.KineticDebugger; import com.simibubi.create.content.contraptions.KineticDebugger;
import com.simibubi.create.content.contraptions.components.structureMovement.ContraptionHandler; import com.simibubi.create.content.contraptions.components.structureMovement.ContraptionHandler;
import com.simibubi.create.content.contraptions.components.structureMovement.bearing.SailBlockPlacementHelper;
import com.simibubi.create.content.contraptions.components.structureMovement.chassis.ChassisRangeDisplay; import com.simibubi.create.content.contraptions.components.structureMovement.chassis.ChassisRangeDisplay;
import com.simibubi.create.content.contraptions.components.structureMovement.piston.PistonPolePlacementHelper;
import com.simibubi.create.content.contraptions.components.structureMovement.train.CouplingHandlerClient; import com.simibubi.create.content.contraptions.components.structureMovement.train.CouplingHandlerClient;
import com.simibubi.create.content.contraptions.components.structureMovement.train.CouplingPhysics; import com.simibubi.create.content.contraptions.components.structureMovement.train.CouplingPhysics;
import com.simibubi.create.content.contraptions.components.structureMovement.train.CouplingRenderer; import com.simibubi.create.content.contraptions.components.structureMovement.train.CouplingRenderer;
@ -36,7 +31,7 @@ import com.simibubi.create.foundation.tileEntity.behaviour.linked.LinkRenderer;
import com.simibubi.create.foundation.tileEntity.behaviour.scrollvalue.ScrollValueRenderer; import com.simibubi.create.foundation.tileEntity.behaviour.scrollvalue.ScrollValueRenderer;
import com.simibubi.create.foundation.utility.AnimationTickHolder; import com.simibubi.create.foundation.utility.AnimationTickHolder;
import com.simibubi.create.foundation.utility.ServerSpeedProvider; import com.simibubi.create.foundation.utility.ServerSpeedProvider;
import com.simibubi.create.foundation.utility.placement.PlacementHelpers;
import net.minecraft.client.Minecraft; import net.minecraft.client.Minecraft;
import net.minecraft.client.renderer.ActiveRenderInfo; import net.minecraft.client.renderer.ActiveRenderInfo;
import net.minecraft.client.renderer.IRenderTypeBuffer; import net.minecraft.client.renderer.IRenderTypeBuffer;
@ -61,6 +56,9 @@ import net.minecraftforge.event.world.WorldEvent;
import net.minecraftforge.eventbus.api.SubscribeEvent; import net.minecraftforge.eventbus.api.SubscribeEvent;
import net.minecraftforge.fml.common.Mod.EventBusSubscriber; import net.minecraftforge.fml.common.Mod.EventBusSubscriber;
import java.util.ArrayList;
import java.util.List;
@EventBusSubscriber(value = Dist.CLIENT) @EventBusSubscriber(value = Dist.CLIENT)
public class ClientEvents { public class ClientEvents {
@ -103,8 +101,7 @@ public class ClientEvents {
ExtendoGripRenderHandler.tick(); ExtendoGripRenderHandler.tick();
// CollisionDebugger.tick(); // CollisionDebugger.tick();
ArmInteractionPointHandler.tick(); ArmInteractionPointHandler.tick();
SailBlockPlacementHelper.tick(); PlacementHelpers.tick();
PistonPolePlacementHelper.tick();
CreateClient.outliner.tickOutlines(); CreateClient.outliner.tickOutlines();
} }
@ -137,8 +134,8 @@ public class ClientEvents {
return; return;
onRenderHotbar(new MatrixStack(), Minecraft.getInstance() onRenderHotbar(new MatrixStack(), Minecraft.getInstance()
.getBufferBuilders() .getBufferBuilders()
.getEntityVertexConsumers(), 0xF000F0, OverlayTexture.DEFAULT_UV); .getEntityVertexConsumers(), 0xF000F0, OverlayTexture.DEFAULT_UV);
} }
public static void onRenderHotbar(MatrixStack ms, IRenderTypeBuffer buffer, int light, int overlay) { public static void onRenderHotbar(MatrixStack ms, IRenderTypeBuffer buffer, int light, int overlay) {
@ -154,7 +151,7 @@ public class ClientEvents {
ItemStack stack = event.getItemStack(); ItemStack stack = event.getItemStack();
String translationKey = stack.getItem() String translationKey = stack.getItem()
.getTranslationKey(stack); .getTranslationKey(stack);
if (!translationKey.startsWith(itemPrefix) && !translationKey.startsWith(blockPrefix)) if (!translationKey.startsWith(itemPrefix) && !translationKey.startsWith(blockPrefix))
return; return;
@ -163,7 +160,7 @@ public class ClientEvents {
List<ITextComponent> toolTip = new ArrayList<>(); List<ITextComponent> toolTip = new ArrayList<>();
toolTip.add(itemTooltip.remove(0)); toolTip.add(itemTooltip.remove(0));
TooltipHelper.getTooltip(stack) TooltipHelper.getTooltip(stack)
.addInformation(toolTip); .addInformation(toolTip);
itemTooltip.addAll(0, toolTip); itemTooltip.addAll(0, toolTip);
} }

View file

@ -5,6 +5,7 @@ import net.minecraft.util.Direction.Axis;
import net.minecraft.util.math.BlockPos; import net.minecraft.util.math.BlockPos;
import java.util.Arrays; import java.util.Arrays;
import java.util.EnumSet;
import java.util.List; import java.util.List;
public class Iterate { public class Iterate {
@ -15,6 +16,7 @@ public class Iterate {
public static final Direction[] directions = Direction.values(); public static final Direction[] directions = Direction.values();
public static final Direction[] horizontalDirections = getHorizontals(); public static final Direction[] horizontalDirections = getHorizontals();
public static final Axis[] axes = Axis.values(); public static final Axis[] axes = Axis.values();
public static final EnumSet<Direction.Axis> axisSet = EnumSet.allOf(Direction.Axis.class);
private static Direction[] getHorizontals() { private static Direction[] getHorizontals() {
Direction[] directions = new Direction[4]; Direction[] directions = new Direction[4];

View file

@ -0,0 +1,111 @@
package com.simibubi.create.foundation.utility.placement;
import com.simibubi.create.CreateClient;
import com.simibubi.create.foundation.utility.Iterate;
import com.simibubi.create.foundation.utility.Pair;
import com.simibubi.create.foundation.utility.VecHelper;
import mcp.MethodsReturnNonnullByDefault;
import net.minecraft.block.BlockState;
import net.minecraft.item.ItemStack;
import net.minecraft.util.Direction;
import net.minecraft.util.math.BlockPos;
import net.minecraft.util.math.BlockRayTraceResult;
import net.minecraft.util.math.Vec3d;
import net.minecraft.world.World;
import java.util.Arrays;
import java.util.Comparator;
import java.util.List;
import java.util.function.Predicate;
import java.util.stream.Collectors;
@MethodsReturnNonnullByDefault
public interface IPlacementHelper {
/**
* @return a predicate that gets tested with the items held in the players hands,
* should return true if this placement helper is active with the given item
*/
Predicate<ItemStack> getItemPredicate();
/**
* @return a predicate that gets tested with the blockstate the player is looking at
* should return true if this placement helper is active with the given blockstate
*/
Predicate<BlockState> getStatePredicate();
/**
* @return PlacementOffset.fail() if no valid offset could be found.
* PlacementOffset.success(newPos) with newPos being the new position the block should be placed at
*/
PlacementOffset getOffset(World world, BlockState state, BlockPos pos, BlockRayTraceResult ray);
//only gets called when placementOffset is successful
default void renderAt(BlockPos pos, BlockState state, BlockRayTraceResult ray, PlacementOffset offset) {
IPlacementHelper.renderArrow(VecHelper.getCenterOf(pos), VecHelper.getCenterOf(offset.getPos()), ray.getFace());
}
static void renderArrow(Vec3d center, Vec3d target, Direction arrowPlane) {
renderArrow(center, target, arrowPlane, 1D);
}
static void renderArrow(Vec3d center, Vec3d target, Direction arrowPlane, double distanceFromCenter) {
Vec3d direction = target.subtract(center).normalize();
Vec3d facing = new Vec3d(arrowPlane.getDirectionVec());
Vec3d start = center.add(direction);
Vec3d offset = direction.scale(distanceFromCenter-1);
Vec3d offsetA = direction.crossProduct(facing).normalize().scale(.25);
Vec3d offsetB = facing.crossProduct(direction).normalize().scale(.25);
Vec3d endA = center.add(direction.scale(.75)).add(offsetA);
Vec3d endB = center.add(direction.scale(.75)).add(offsetB);
CreateClient.outliner.showLine("placementArrowA" + center + target, start.add(offset), endA.add(offset)).lineWidth(1/16f);
CreateClient.outliner.showLine("placementArrowB" + center + target, start.add(offset), endB.add(offset)).lineWidth(1/16f);
}
/*@OnlyIn(Dist.CLIENT)
static void renderArrow(Vec3d center, Direction towards, BlockRayTraceResult ray) {
Direction hitFace = ray.getFace();
if (hitFace.getAxis() == towards.getAxis())
return;
//get the two perpendicular directions to form the arrow
Direction[] directions = Arrays.stream(Direction.Axis.values()).filter(axis -> axis != hitFace.getAxis() && axis != towards.getAxis()).map(Iterate::directionsInAxis).findFirst().orElse(new Direction[]{});
Vec3d startOffset = new Vec3d(towards.getDirectionVec());
Vec3d start = center.add(startOffset);
for (Direction dir : directions) {
Vec3d arrowOffset = new Vec3d(dir.getDirectionVec()).scale(.25);
Vec3d target = center.add(startOffset.scale(0.75)).add(arrowOffset);
CreateClient.outliner.showLine("placementArrow" + towards + dir, start, target).lineWidth(1/16f);
}
}*/
static List<Direction> orderedByDistanceOnlyAxis(BlockPos pos, Vec3d hit, Direction.Axis axis) {
return orderedByDistance(pos, hit, dir -> dir.getAxis() == axis);
}
static List<Direction> orderedByDistanceOnlyAxis(BlockPos pos, Vec3d hit, Direction.Axis axis, Predicate<Direction> includeDirection) {
return orderedByDistance(pos, hit, ((Predicate<Direction>) dir -> dir.getAxis() == axis).and(includeDirection));
}
static List<Direction> orderedByDistanceExceptAxis(BlockPos pos, Vec3d hit, Direction.Axis axis) {
return orderedByDistance(pos, hit, dir -> dir.getAxis() != axis);
}
static List<Direction> orderedByDistanceExceptAxis(BlockPos pos, Vec3d hit, Direction.Axis axis, Predicate<Direction> includeDirection) {
return orderedByDistance(pos, hit, ((Predicate<Direction>) dir -> dir.getAxis() != axis).and(includeDirection));
}
static List<Direction> orderedByDistance(BlockPos pos, Vec3d hit) {
return orderedByDistance(pos, hit, _$ -> true);
}
static List<Direction> orderedByDistance(BlockPos pos, Vec3d hit, Predicate<Direction> includeDirection) {
Vec3d centerToHit = hit.subtract(VecHelper.getCenterOf(pos));
return Arrays.stream(Iterate.directions)
.filter(includeDirection)
.map(dir -> Pair.of(dir, new Vec3d(dir.getDirectionVec()).distanceTo(centerToHit)))
.sorted(Comparator.comparingDouble(Pair::getSecond))
.map(Pair::getFirst)
.collect(Collectors.toList());
}
}

View file

@ -0,0 +1,74 @@
package com.simibubi.create.foundation.utility.placement;
import net.minecraft.block.BlockState;
import net.minecraft.client.Minecraft;
import net.minecraft.client.world.ClientWorld;
import net.minecraft.util.Hand;
import net.minecraft.util.math.BlockPos;
import net.minecraft.util.math.BlockRayTraceResult;
import net.minecraftforge.api.distmarker.Dist;
import net.minecraftforge.api.distmarker.OnlyIn;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import java.util.stream.Collectors;
public class PlacementHelpers {
private static final List<IPlacementHelper> helpers = new ArrayList<>();
public static int register(IPlacementHelper helper) {
helpers.add(helper);
return helpers.size() - 1;
}
public static IPlacementHelper get(int id) {
if (id < 0 || id >= helpers.size())
throw new ArrayIndexOutOfBoundsException("id " + id + " for placement helper not known");
return helpers.get(id);
}
@OnlyIn(Dist.CLIENT)
public static void tick() {
Minecraft mc = Minecraft.getInstance();
ClientWorld world = mc.world;
if (world == null)
return;
if (!(mc.objectMouseOver instanceof BlockRayTraceResult))
return;
BlockRayTraceResult ray = (BlockRayTraceResult) mc.objectMouseOver;
if (mc.player == null)
return;
List<IPlacementHelper> filteredForHeldItem = helpers.stream().filter(helper -> Arrays.stream(Hand.values()).anyMatch(hand -> helper.getItemPredicate().test(mc.player.getHeldItem(hand)))).collect(Collectors.toList());
if (filteredForHeldItem.isEmpty())
return;
if (mc.player.isSneaking())//for now, disable all helpers when sneaking TODO add helpers that respect sneaking but still show position
return;
BlockPos pos = ray.getPos();
BlockState state = world.getBlockState(pos);
List<IPlacementHelper> filteredForState = filteredForHeldItem.stream().filter(helper -> helper.getStatePredicate().test(state)).collect(Collectors.toList());
if (filteredForState.isEmpty())
return;
for (IPlacementHelper h : filteredForState) {
PlacementOffset offset = h.getOffset(world, state, pos, ray);
if (offset.isSuccessful()) {
h.renderAt(pos, state, ray, offset);
break;
}
}
}
}

View file

@ -0,0 +1,43 @@
package com.simibubi.create.foundation.utility.placement;
import net.minecraft.block.BlockState;
import net.minecraft.util.math.Vec3i;
import java.util.function.Function;
public class PlacementOffset {
private final boolean success;
private final Vec3i pos;
private final Function<BlockState, BlockState> stateTransform;
private PlacementOffset(boolean success, Vec3i pos, Function<BlockState, BlockState> transform) {
this.success = success;
this.pos = pos;
this.stateTransform = transform == null ? Function.identity() : transform;
}
public static PlacementOffset fail() {
return new PlacementOffset(false, Vec3i.NULL_VECTOR, null);
}
public static PlacementOffset success(Vec3i pos) {
return new PlacementOffset(true, pos, null);
}
public static PlacementOffset success(Vec3i pos, Function<BlockState, BlockState> transform) {
return new PlacementOffset(true, pos, transform);
}
public boolean isSuccessful() {
return success;
}
public Vec3i getPos() {
return pos;
}
public Function<BlockState, BlockState> getTransform() {
return stateTransform;
}
}

View file

@ -0,0 +1,77 @@
package com.simibubi.create.foundation.utility.placement.util;
import com.simibubi.create.foundation.utility.VecHelper;
import com.simibubi.create.foundation.utility.placement.IPlacementHelper;
import com.simibubi.create.foundation.utility.placement.PlacementOffset;
import mcp.MethodsReturnNonnullByDefault;
import net.minecraft.block.BlockState;
import net.minecraft.state.IProperty;
import net.minecraft.util.Direction;
import net.minecraft.util.math.BlockPos;
import net.minecraft.util.math.BlockRayTraceResult;
import net.minecraft.util.math.Vec3d;
import net.minecraft.world.World;
import java.util.List;
import java.util.function.Function;
import java.util.function.Predicate;
@MethodsReturnNonnullByDefault
public abstract class PoleHelper<T extends Comparable<T>> implements IPlacementHelper {
protected final Predicate<BlockState> statePredicate;
protected final IProperty<T> property;
protected final Function<BlockState, Direction.Axis> axisFunction;
public PoleHelper(Predicate<BlockState> statePredicate, Function<BlockState, Direction.Axis> axisFunction, IProperty<T> property) {
this.statePredicate = statePredicate;
this.axisFunction = axisFunction;
this.property = property;
}
public boolean matchesAxis(BlockState state, Direction.Axis axis) {
if (!statePredicate.test(state))
return false;
return axisFunction.apply(state) == axis;
}
public int attachedPoles(World world, BlockPos pos, Direction direction) {
BlockPos checkPos = pos.offset(direction);
BlockState state = world.getBlockState(checkPos);
int count = 0;
while (matchesAxis(state, direction.getAxis())) {
count++;
checkPos = checkPos.offset(direction);
state = world.getBlockState(checkPos);
}
return count;
}
@Override
public Predicate<BlockState> getStatePredicate() {
return this.statePredicate;
}
@Override
public PlacementOffset getOffset(World world, BlockState state, BlockPos pos, BlockRayTraceResult ray) {
List<Direction> directions = IPlacementHelper.orderedByDistance(pos, ray.getHitVec(), dir -> dir.getAxis() == axisFunction.apply(state));
for (Direction dir : directions) {
int poles = attachedPoles(world, pos, dir);
BlockPos newPos = pos.offset(dir, poles + 1);
BlockState newState = world.getBlockState(newPos);
if (newState.getMaterial().isReplaceable())
return PlacementOffset.success(newPos, bState -> bState.with(property, state.get(property)));
}
return PlacementOffset.fail();
}
@Override
public void renderAt(BlockPos pos, BlockState state, BlockRayTraceResult ray, PlacementOffset offset) {
Vec3d centerOffset = new Vec3d(ray.getFace().getDirectionVec()).scale(.3);
IPlacementHelper.renderArrow(VecHelper.getCenterOf(pos).add(centerOffset), VecHelper.getCenterOf(offset.getPos()).add(centerOffset), ray.getFace(), 0.75D);
}
}