Improve cable/interface placement/breaking logic.

Can now place interfaces on their own, add cable later.
Can remove just cable via wrench.
This commit is contained in:
Florian Nücke
2021-02-09 00:58:26 +01:00
parent 41acc8b728
commit 1996a8addc
16 changed files with 451 additions and 181 deletions

View File

@@ -35,7 +35,7 @@ public final class BusCableBakedModel implements IDynamicBakedModel {
@Override
public List<BakedQuad> getQuads(@Nullable final BlockState state, @Nullable final Direction side, final Random rand, final IModelData extraData) {
if (state == null) {
if (state == null || !state.get(BusCableBlock.HAS_CABLE)) {
return proxy.getQuads(null, side, rand, extraData);
}
@@ -96,7 +96,7 @@ public final class BusCableBakedModel implements IDynamicBakedModel {
for (final Direction direction : Constants.DIRECTIONS) {
if (isNeighborInDirectionSolid(world, pos, direction)) {
final EnumProperty<BusCableBlock.ConnectionType> property = BusCableBlock.FACING_TO_CONNECTION_MAP.get(direction);
if (state.hasProperty(property) && state.get(property) == BusCableBlock.ConnectionType.PLUG) {
if (state.hasProperty(property) && state.get(property) == BusCableBlock.ConnectionType.INTERFACE) {
return tileData; // Plug is already supporting us, bail.
}
@@ -126,7 +126,7 @@ public final class BusCableBakedModel implements IDynamicBakedModel {
for (final Direction direction : Constants.DIRECTIONS) {
final EnumProperty<BusCableBlock.ConnectionType> property = BusCableBlock.FACING_TO_CONNECTION_MAP.get(direction);
if (axis.test(direction)) {
if (state.get(property) != BusCableBlock.ConnectionType.LINK) {
if (state.get(property) != BusCableBlock.ConnectionType.CABLE) {
return false;
}
} else {

View File

@@ -16,41 +16,46 @@ import net.minecraft.entity.player.PlayerEntity;
import net.minecraft.item.BlockItemUseContext;
import net.minecraft.item.ItemStack;
import net.minecraft.loot.LootContext;
import net.minecraft.state.BooleanProperty;
import net.minecraft.state.EnumProperty;
import net.minecraft.state.StateContainer;
import net.minecraft.tileentity.TileEntity;
import net.minecraft.util.*;
import net.minecraft.util.math.AxisAlignedBB;
import net.minecraft.util.math.BlockPos;
import net.minecraft.util.math.BlockRayTraceResult;
import net.minecraft.util.math.shapes.ISelectionContext;
import net.minecraft.util.math.shapes.VoxelShape;
import net.minecraft.util.math.shapes.VoxelShapes;
import net.minecraft.util.math.vector.Vector3d;
import net.minecraft.util.math.vector.Vector3i;
import net.minecraft.world.GameRules;
import net.minecraft.world.IBlockReader;
import net.minecraft.world.IWorld;
import net.minecraft.world.World;
import net.minecraftforge.common.util.Constants.BlockFlags;
import javax.annotation.Nullable;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import java.util.Map;
public final class BusCableBlock extends Block {
public enum ConnectionType implements IStringSerializable {
NONE,
LINK,
PLUG;
CABLE,
INTERFACE;
@Override
public String getString() {
switch (this) {
case NONE:
return "none";
case LINK:
return "link";
case PLUG:
return "plug";
case CABLE:
return "cable";
case INTERFACE:
return "interface";
default:
throw new IllegalArgumentException();
}
@@ -59,6 +64,7 @@ public final class BusCableBlock extends Block {
///////////////////////////////////////////////////////////////////
public static final BooleanProperty HAS_CABLE = BooleanProperty.create("has_cable");
public static final EnumProperty<ConnectionType> CONNECTION_NORTH = EnumProperty.create("connection_north", ConnectionType.class);
public static final EnumProperty<ConnectionType> CONNECTION_EAST = EnumProperty.create("connection_east", ConnectionType.class);
public static final EnumProperty<ConnectionType> CONNECTION_SOUTH = EnumProperty.create("connection_south", ConnectionType.class);
@@ -99,6 +105,7 @@ public final class BusCableBlock extends Block {
for (final EnumProperty<ConnectionType> property : FACING_TO_CONNECTION_MAP.values()) {
defaultState = defaultState.with(property, ConnectionType.NONE);
}
defaultState = defaultState.with(HAS_CABLE, true);
setDefaultState(defaultState);
shapes = makeShapes();
@@ -106,18 +113,29 @@ public final class BusCableBlock extends Block {
///////////////////////////////////////////////////////////////////
public boolean addPlug(final World world, final BlockPos pos, final BlockState state, final Direction side) {
final EnumProperty<BusCableBlock.ConnectionType> property = BusCableBlock.FACING_TO_CONNECTION_MAP.get(side);
if (state.get(property) == BusCableBlock.ConnectionType.NONE) {
if (!world.isRemote()) {
world.setBlockState(pos, state.with(property, ConnectionType.PLUG));
onConnectionTypeChanged(world, pos, side);
WorldUtils.playSound(world, pos, state.getSoundType(), SoundType::getPlaceSound);
}
return true;
public boolean addInterface(final World world, final BlockPos pos, final BlockState state, final Direction side) {
final EnumProperty<BusCableBlock.ConnectionType> property = FACING_TO_CONNECTION_MAP.get(side);
if (state.get(property) != ConnectionType.NONE) {
return false;
}
return false;
world.setBlockState(pos, state.with(property, ConnectionType.INTERFACE), BlockFlags.DEFAULT_AND_RERENDER);
onConnectionTypeChanged(world, pos, side);
return true;
}
public boolean addCable(final World world, final BlockPos pos, final BlockState state) {
if (state.get(HAS_CABLE)) {
return false;
}
world.setBlockState(pos, state.with(HAS_CABLE, true), BlockFlags.DEFAULT_AND_RERENDER);
onConnectionTypeChanged(world, pos, null);
return true;
}
@Override
@@ -144,11 +162,17 @@ public final class BusCableBlock extends Block {
@SuppressWarnings("deprecation")
@Override
public ActionResultType onBlockActivated(final BlockState state, final World world, final BlockPos pos, final PlayerEntity player, final Hand hand, final BlockRayTraceResult hit) {
if (Wrenches.isWrench(player.getHeldItem(hand)) && tryRemovePlug(state, world, pos, player, hit)) {
return ActionResultType.SUCCESS;
if (player.isSneaking() && Wrenches.isWrench(player.getHeldItem(hand))) {
// NB: leave wrenching logic up to wrench when the to-be-removed interface is the last
// part of this bus. This ensures we properly remove the block itself without having
// to duplicate the logic needed for that.
if (getPartCount(state) > 1)
if (tryRemovePlug(state, world, pos, player, hit) || tryRemoveCable(state, world, pos, player)) {
return ActionResultType.func_233537_a_(world.isRemote());
}
}
return ActionResultType.PASS;
return super.onBlockActivated(state, world, pos, player, hand, hit);
}
@SuppressWarnings("deprecation")
@@ -156,16 +180,20 @@ public final class BusCableBlock extends Block {
public List<ItemStack> getDrops(final BlockState state, final LootContext.Builder builder) {
final List<ItemStack> drops = new ArrayList<>(super.getDrops(state, builder));
int plugCount = 0;
if (state.get(HAS_CABLE)) {
drops.add(new ItemStack(Items.BUS_CABLE.get()));
}
int interfaceCount = 0;
for (final Direction side : Constants.DIRECTIONS) {
final ConnectionType connectionType = state.get(FACING_TO_CONNECTION_MAP.get(side));
if (connectionType == ConnectionType.PLUG) {
plugCount++;
if (connectionType == ConnectionType.INTERFACE) {
interfaceCount++;
}
}
if (plugCount > 0) {
drops.add(new ItemStack(Items.BUS_INTERFACE.get(), plugCount));
if (interfaceCount > 0) {
drops.add(new ItemStack(Items.BUS_INTERFACE.get(), interfaceCount));
}
return drops;
@@ -180,8 +208,9 @@ public final class BusCableBlock extends Block {
for (final Map.Entry<Direction, EnumProperty<ConnectionType>> entry : FACING_TO_CONNECTION_MAP.entrySet()) {
final Direction facing = entry.getKey();
final BlockPos facingPos = position.offset(facing);
if (isCableBlock(world.getBlockState(facingPos))) {
state = state.with(entry.getValue(), ConnectionType.LINK);
if (context.getItem().getItem() == Items.BUS_CABLE.get() &&
canHaveCableTo(world.getBlockState(facingPos), facing.getOpposite())) {
state = state.with(entry.getValue(), ConnectionType.CABLE);
}
}
@@ -191,11 +220,16 @@ public final class BusCableBlock extends Block {
@SuppressWarnings("deprecation")
@Override
public BlockState updatePostPlacement(BlockState state, final Direction facing, final BlockState facingState, final IWorld world, final BlockPos currentPos, final BlockPos facingPos) {
if (isCableBlock(facingState)) {
state = state.with(FACING_TO_CONNECTION_MAP.get(facing), ConnectionType.LINK);
} else if (state.get(FACING_TO_CONNECTION_MAP.get(facing)) != ConnectionType.PLUG) {
if (state.get(FACING_TO_CONNECTION_MAP.get(facing)) == ConnectionType.INTERFACE) {
return state;
}
if (state.get(HAS_CABLE) && canHaveCableTo(facingState, facing.getOpposite())) {
state = state.with(FACING_TO_CONNECTION_MAP.get(facing), ConnectionType.CABLE);
} else {
state = state.with(FACING_TO_CONNECTION_MAP.get(facing), ConnectionType.NONE);
}
onConnectionTypeChanged(world, currentPos, facing);
return state;
@@ -213,62 +247,22 @@ public final class BusCableBlock extends Block {
protected void fillStateContainer(final StateContainer.Builder<Block, BlockState> builder) {
super.fillStateContainer(builder);
FACING_TO_CONNECTION_MAP.values().forEach(builder::add);
builder.add(HAS_CABLE);
}
///////////////////////////////////////////////////////////////////
private VoxelShape[] makeShapes() {
final VoxelShape coreShape = Block.makeCuboidShape(5, 5, 5, 11, 11, 11);
final VoxelShape[] connectionShapes = new VoxelShape[Constants.DIRECTIONS.length];
for (int i = 0; i < Constants.DIRECTIONS.length; i++) {
final Direction direction = Constants.DIRECTIONS[i];
connectionShapes[i] = VoxelShapes.create(
0.5 + Math.min((-2.0 / (16 - 6)), direction.getXOffset() * 0.5),
0.5 + Math.min((-2.0 / (16 - 6)), direction.getYOffset() * 0.5),
0.5 + Math.min((-2.0 / (16 - 6)), direction.getZOffset() * 0.5),
0.5 + Math.max(2.0 / (16 - 6), direction.getXOffset() * 0.5),
0.5 + Math.max(2.0 / (16 - 6), direction.getYOffset() * 0.5),
0.5 + Math.max(2.0 / (16 - 6), direction.getZOffset() * 0.5));
}
final VoxelShape[] result = new VoxelShape[1 << 6];
for (int i = 0; i < result.length; i++) {
VoxelShape shape = coreShape;
for (int j = 0; j < Constants.DIRECTIONS.length; j++) {
if ((i & (1 << j)) != 0) {
shape = VoxelShapes.or(shape, connectionShapes[j]);
}
}
result[i] = shape;
}
return result;
private boolean canHaveCableTo(final BlockState state, final Direction side) {
return state.getBlock() == this && state.get(HAS_CABLE) && state.get(FACING_TO_CONNECTION_MAP.get(side)) != ConnectionType.INTERFACE;
}
private int getShapeIndex(final BlockState state) {
int index = 0;
for (int i = 0; i < Constants.DIRECTIONS.length; i++) {
if (state.get(FACING_TO_CONNECTION_MAP.get(Constants.DIRECTIONS[i])) != ConnectionType.NONE) {
index |= 1 << i;
}
}
return index;
}
private boolean isCableBlock(final BlockState state) {
return state.getBlock() == this;
}
private void onConnectionTypeChanged(final IWorld world, final BlockPos pos, final Direction face) {
final TileEntity tileEntity = world.getTileEntity(pos);
if (tileEntity instanceof BusCableTileEntity) {
final BusCableTileEntity busCable = (BusCableTileEntity) tileEntity;
busCable.handleConnectionTypeChanged(face);
private static int getPartCount(final BlockState state) {
int partCount = 0;
if (state.get(HAS_CABLE)) partCount++;
for (final EnumProperty<ConnectionType> connectionType : FACING_TO_CONNECTION_MAP.values()) {
if (state.get(connectionType) == ConnectionType.INTERFACE) partCount++;
}
return partCount;
}
private boolean tryRemovePlug(final BlockState state, final World world, final BlockPos pos, final PlayerEntity player, final BlockRayTraceResult hit) {
@@ -276,27 +270,169 @@ public final class BusCableBlock extends Block {
final Direction side = Direction.getFacingFromVector(localHitPos.x, localHitPos.y, localHitPos.z);
final EnumProperty<ConnectionType> property = FACING_TO_CONNECTION_MAP.get(side);
if (state.get(property) != ConnectionType.PLUG) {
if (state.get(property) != ConnectionType.INTERFACE) {
return false;
}
final BlockPos neighborPos = pos.offset(side);
if (isCableBlock(world.getBlockState(neighborPos))) {
world.setBlockState(pos, state.with(property, ConnectionType.LINK));
if (state.get(HAS_CABLE) && canHaveCableTo(world.getBlockState(neighborPos), side.getOpposite())) {
world.setBlockState(pos, state.with(property, ConnectionType.CABLE));
} else {
world.setBlockState(pos, state.with(property, ConnectionType.NONE));
}
handlePartRemoved(state, world, pos, side, player, new ItemStack(Items.BUS_INTERFACE.get()));
return true;
}
private boolean tryRemoveCable(final BlockState state, final World world, final BlockPos pos, final PlayerEntity player) {
if (!state.get(HAS_CABLE)) {
return false;
}
world.setBlockState(pos, state.with(HAS_CABLE, false));
handlePartRemoved(state, world, pos, null, player, new ItemStack(Items.BUS_CABLE.get()));
return true;
}
private void handlePartRemoved(final BlockState state, final World world, final BlockPos pos, @Nullable final Direction side, final PlayerEntity player, final ItemStack drop) {
onConnectionTypeChanged(world, pos, side);
if (!player.isCreative() && world.getGameRules().getBoolean(GameRules.DO_TILE_DROPS)) {
ItemStackUtils.spawnAsEntity(world, pos, new ItemStack(Items.BUS_INTERFACE.get()), side).ifPresent(entity -> {
ItemStackUtils.spawnAsEntity(world, pos, drop, side).ifPresent(entity -> {
entity.setNoPickupDelay();
entity.onCollideWithPlayer(player);
});
}
WorldUtils.playSound(world, pos, state.getSoundType(), SoundType::getBreakSound);
}
return true;
private void onConnectionTypeChanged(final IWorld world, final BlockPos pos, @Nullable final Direction face) {
final TileEntity tileEntity = world.getTileEntity(pos);
if (tileEntity instanceof BusCableTileEntity) {
final BusCableTileEntity busCable = (BusCableTileEntity) tileEntity;
busCable.handleConnectivityChanged(face);
}
}
private static VoxelShape[] makeShapes() {
final VoxelShape ownCableBounds = Block.makeCuboidShape(5, 5, 5, 11, 11, 11);
final VoxelShape[] cableShapes = new VoxelShape[Constants.BLOCK_FACE_COUNT];
final VoxelShape[] interfaceShapes = new VoxelShape[Constants.BLOCK_FACE_COUNT];
for (int i = 0; i < Constants.BLOCK_FACE_COUNT; i++) {
cableShapes[i] = getCableShape(Constants.DIRECTIONS[i]);
interfaceShapes[i] = getInterfaceShape(Constants.DIRECTIONS[i]);
}
// We pack info as such:
// [has interface on side] [has cable on side] [has own cable]
// Which is a total of 13 bits (6 + 6 + 1), so 8k combinations.
// It's a lot, but not so much that it's not ok to still cache
// it and avoid recomputing the bounds all the time.
final int configurations = 1 << (6 + 6 + 1);
final VoxelShape[] result = new VoxelShape[configurations];
Arrays.fill(result, VoxelShapes.empty());
for (int i = 0; i < result.length; i++) {
final int mask = i >> 1;
for (int sideIndex = 0; sideIndex < Constants.BLOCK_FACE_COUNT; sideIndex++) {
final int cableBit = 1 << sideIndex;
if ((mask & cableBit) != 0) {
result[i] = VoxelShapes.or(result[i], cableShapes[sideIndex]);
}
final int interfaceBit = cableBit << 6;
if ((mask & interfaceBit) != 0) {
result[i] = VoxelShapes.or(result[i], interfaceShapes[sideIndex]);
}
}
if ((i & 1) != 0) {
result[i] = VoxelShapes.or(result[i], ownCableBounds);
}
}
return result;
}
private static VoxelShape getCableShape(final Direction zDirection) {
final int xSize = 6;
final int ySize = 6;
final int zSize = 5;
final Direction yDirection = zDirection.getAxis() == Direction.Axis.Y ? Direction.NORTH : Direction.UP;
final Direction xDirection = zDirection.getAxis() == Direction.Axis.Y ? Direction.WEST : zDirection.rotateY();
final Vector3i min = new Vector3i(8, 8, 8)
.offset(xDirection, -xSize / 2)
.offset(yDirection, -ySize / 2)
.offset(zDirection, 8 - zSize);
final Vector3i max = new Vector3i(8, 8, 8)
.offset(xDirection, xSize / 2)
.offset(yDirection, ySize / 2)
.offset(zDirection, 8);
final AxisAlignedBB bounds = new AxisAlignedBB(
Vector3d.copy(min).scale(1 / 16.0),
Vector3d.copy(max).scale(1 / 16.0)
);
return VoxelShapes.create(bounds);
}
private static VoxelShape getInterfaceShape(final Direction zDirection) {
final int xSize = 8;
final int ySize = 8;
final int zSize = 1;
final Direction yDirection = zDirection.getAxis() == Direction.Axis.Y ? Direction.NORTH : Direction.UP;
final Direction xDirection = zDirection.getAxis() == Direction.Axis.Y ? Direction.WEST : zDirection.rotateY();
final Vector3i min = new Vector3i(8, 8, 8)
.offset(xDirection, -xSize / 2)
.offset(yDirection, -ySize / 2)
.offset(zDirection, 8 - zSize);
final Vector3i max = new Vector3i(8, 8, 8)
.offset(xDirection, xSize / 2)
.offset(yDirection, ySize / 2)
.offset(zDirection, 8);
final AxisAlignedBB bounds = new AxisAlignedBB(
Vector3d.copy(min).scale(1 / 16.0),
Vector3d.copy(max).scale(1 / 16.0)
);
return VoxelShapes.or(getCableShape(zDirection), VoxelShapes.create(bounds));
}
private static int getShapeIndex(final BlockState state) {
int index = 0;
for (int sideIndex = 0; sideIndex < Constants.BLOCK_FACE_COUNT; sideIndex++) {
final int cableBit = 1 << sideIndex;
final int interfaceBit = cableBit << 6;
switch (state.get(FACING_TO_CONNECTION_MAP.get(Constants.DIRECTIONS[sideIndex]))) {
case CABLE:
index |= cableBit;
break;
case INTERFACE:
index |= interfaceBit;
break;
}
}
index = index << 1;
if (state.get(HAS_CABLE)) {
index |= 1;
}
return index;
}
}

View File

@@ -61,6 +61,7 @@ import net.minecraftforge.api.distmarker.OnlyIn;
import net.minecraftforge.common.MinecraftForge;
import net.minecraftforge.common.capabilities.Capability;
import net.minecraftforge.common.capabilities.ICapabilityProvider;
import net.minecraftforge.common.util.Constants.BlockFlags;
import net.minecraftforge.common.util.LazyOptional;
import net.minecraftforge.event.world.ChunkEvent;
import net.minecraftforge.event.world.WorldEvent;
@@ -270,7 +271,7 @@ public final class RobotEntity extends Entity implements Robot {
.withParameter(LootParameters.BLOCK_STATE, blockState)
.withNullableParameter(LootParameters.BLOCK_ENTITY, tileEntity);
final List<ItemStack> drops = blockState.getDrops(builder);
world.setBlockState(mutablePosition, Blocks.AIR.getDefaultState(), 3);
world.setBlockState(mutablePosition, Blocks.AIR.getDefaultState());
for (final ItemStack drop : drops) {
Block.spawnAsEntity(world, mutablePosition, drop);
}

View File

@@ -0,0 +1,66 @@
package li.cil.oc2.common.item;
import li.cil.oc2.common.block.Blocks;
import li.cil.oc2.common.block.BusCableBlock;
import li.cil.oc2.common.util.WorldUtils;
import net.minecraft.advancements.CriteriaTriggers;
import net.minecraft.block.Block;
import net.minecraft.block.BlockState;
import net.minecraft.block.SoundType;
import net.minecraft.entity.player.PlayerEntity;
import net.minecraft.entity.player.ServerPlayerEntity;
import net.minecraft.item.BlockItemUseContext;
import net.minecraft.item.ItemStack;
import net.minecraft.item.ItemUseContext;
import net.minecraft.util.ActionResultType;
import net.minecraft.util.math.BlockPos;
import net.minecraft.world.World;
public final class BusCableItem extends ModBlockItem {
public BusCableItem(final Block block) {
super(block);
}
///////////////////////////////////////////////////////////////////
@Override
public ActionResultType onItemUse(final ItemUseContext context) {
final ActionResultType result = tryAddToBlock(context);
return result.isSuccessOrConsume() ? result : super.onItemUse(context);
}
@Override
public ActionResultType tryPlace(final BlockItemUseContext context) {
final ActionResultType result = tryAddToBlock(context);
return result.isSuccessOrConsume() ? result : super.tryPlace(context);
}
///////////////////////////////////////////////////////////////////
private ActionResultType tryAddToBlock(final ItemUseContext context) {
final BusCableBlock busCableBlock = Blocks.BUS_CABLE.get();
final World world = context.getWorld();
final BlockPos pos = context.getPos();
final BlockState state = world.getBlockState(pos);
if (state.getBlock() == busCableBlock && busCableBlock.addCable(world, pos, state)) {
final PlayerEntity player = context.getPlayer();
final ItemStack stack = context.getItem();
if (player instanceof ServerPlayerEntity) {
CriteriaTriggers.PLACED_BLOCK.trigger((ServerPlayerEntity) player, pos, stack);
}
WorldUtils.playSound(world, pos, state.getSoundType(world, pos, player), SoundType::getPlaceSound);
if (player == null || !player.abilities.isCreativeMode) {
stack.shrink(1);
}
return ActionResultType.func_233537_a_(world.isRemote);
}
return ActionResultType.PASS;
}
}

View File

@@ -2,47 +2,85 @@ package li.cil.oc2.common.item;
import li.cil.oc2.common.block.Blocks;
import li.cil.oc2.common.block.BusCableBlock;
import li.cil.oc2.common.block.BusCableBlock.ConnectionType;
import li.cil.oc2.common.util.WorldUtils;
import net.minecraft.advancements.CriteriaTriggers;
import net.minecraft.block.Block;
import net.minecraft.block.BlockState;
import net.minecraft.block.SoundType;
import net.minecraft.entity.player.PlayerEntity;
import net.minecraft.entity.player.ServerPlayerEntity;
import net.minecraft.item.BlockItemUseContext;
import net.minecraft.item.ItemStack;
import net.minecraft.item.ItemUseContext;
import net.minecraft.state.EnumProperty;
import net.minecraft.util.ActionResultType;
import net.minecraft.util.Direction;
import net.minecraft.util.math.BlockPos;
import net.minecraft.util.math.vector.Vector3d;
import net.minecraft.world.World;
import org.jetbrains.annotations.Nullable;
public final class BusInterfaceItem extends ModBlockItem {
public BusInterfaceItem(final Block block) {
super(block);
}
///////////////////////////////////////////////////////////////////
public final class BusInterfaceItem extends ModItem {
@Override
public ActionResultType onItemUse(final ItemUseContext context) {
final Vector3d localHitPos = context.getHitVec().subtract(Vector3d.copyCentered(context.getPos()));
final Direction side = Direction.getFacingFromVector(localHitPos.x, localHitPos.y, localHitPos.z);
final ActionResultType result = tryAddToBlock(context, side);
return result.isSuccessOrConsume() ? result : super.onItemUse(context);
}
@Override
public ActionResultType tryPlace(final BlockItemUseContext context) {
final ActionResultType result = tryAddToBlock(context, context.getFace().getOpposite());
return result.isSuccessOrConsume() ? result : super.tryPlace(context);
}
///////////////////////////////////////////////////////////////////
@Nullable
@Override
protected BlockState getStateForPlacement(final BlockItemUseContext context) {
final BlockState state = super.getStateForPlacement(context);
final EnumProperty<ConnectionType> connectionTypeProperty =
BusCableBlock.FACING_TO_CONNECTION_MAP.get(context.getFace().getOpposite());
return state
.with(BusCableBlock.HAS_CABLE, false)
.with(connectionTypeProperty, ConnectionType.INTERFACE);
}
///////////////////////////////////////////////////////////////////
private ActionResultType tryAddToBlock(final ItemUseContext context, final Direction side) {
final BusCableBlock busCableBlock = Blocks.BUS_CABLE.get();
final World world = context.getWorld();
final BlockPos pos = context.getPos();
final BlockState state = world.getBlockState(pos);
final BusCableBlock busCableBlock = Blocks.BUS_CABLE.get();
if (state.getBlock() == busCableBlock) {
final Vector3d localHitPos = context.getHitVec().subtract(Vector3d.copyCentered(pos));
final Direction side = Direction.getFacingFromVector(localHitPos.x, localHitPos.y, localHitPos.z);
if (busCableBlock.addPlug(world, pos, state, side)) {
context.getItem().shrink(1);
return ActionResultType.SUCCESS;
}
} else {
final BlockPos neighborPos = pos.offset(context.getFace());
final BlockState neighborState = world.getBlockState(neighborPos);
if (state.getBlock() == busCableBlock && busCableBlock.addInterface(world, pos, state, side)) {
final PlayerEntity player = context.getPlayer();
final ItemStack stack = context.getItem();
// TODO Store in cable whether cable itself is present, i.e. allow bus blocks that are just plugs.
// if (neighborState.isReplaceable(new BlockItemUseContext(context))) {
// world.setBlockState(neighborPos, Blocks.BUS_CABLE.get().getDefaultState());
// }
if (neighborState.getBlock() == busCableBlock) {
final Direction side = context.getFace().getOpposite();
if (busCableBlock.addPlug(world, neighborPos, neighborState, side)) {
context.getItem().shrink(1);
return ActionResultType.SUCCESS;
}
if (player instanceof ServerPlayerEntity) {
CriteriaTriggers.PLACED_BLOCK.trigger((ServerPlayerEntity) player, pos, stack);
}
WorldUtils.playSound(world, pos, state.getSoundType(world, pos, player), SoundType::getPlaceSound);
if (player == null || !player.abilities.isCreativeMode) {
stack.shrink(1);
}
return ActionResultType.func_233537_a_(world.isRemote);
}
return super.onItemUse(context);
return ActionResultType.PASS;
}
}

View File

@@ -10,6 +10,7 @@ import net.minecraftforge.fml.javafmlmod.FMLJavaModLoadingContext;
import net.minecraftforge.registries.DeferredRegister;
import net.minecraftforge.registries.ForgeRegistries;
import java.util.function.Function;
import java.util.function.Supplier;
public final class Items {
@@ -18,7 +19,8 @@ public final class Items {
///////////////////////////////////////////////////////////////////
public static final RegistryObject<Item> COMPUTER = register(Constants.COMPUTER_BLOCK_NAME, Blocks.COMPUTER);
public static final RegistryObject<Item> BUS_CABLE = register(Constants.BUS_CABLE_BLOCK_NAME, Blocks.BUS_CABLE);
public static final RegistryObject<Item> BUS_CABLE = register(Constants.BUS_CABLE_BLOCK_NAME, Blocks.BUS_CABLE, BusCableItem::new);
public static final RegistryObject<BusInterfaceItem> BUS_INTERFACE = register(Constants.BUS_INTERFACE_ITEM_NAME, Blocks.BUS_CABLE, BusInterfaceItem::new);
public static final RegistryObject<Item> NETWORK_CONNECTOR = register(Constants.NETWORK_CONNECTOR_BLOCK_NAME, Blocks.NETWORK_CONNECTOR);
public static final RegistryObject<Item> NETWORK_HUB = register(Constants.NETWORK_HUB_BLOCK_NAME, Blocks.NETWORK_HUB);
public static final RegistryObject<Item> REDSTONE_INTERFACE = register(Constants.REDSTONE_INTERFACE_BLOCK_NAME, Blocks.REDSTONE_INTERFACE);
@@ -28,8 +30,7 @@ public final class Items {
public static final RegistryObject<Item> WRENCH = register(Constants.WRENCH_ITEM_NAME, WrenchItem::new);
public static final RegistryObject<Item> BUS_INTERFACE = register(Constants.BUS_INTERFACE_ITEM_NAME, BusInterfaceItem::new);
public static final RegistryObject<Item> NETWORK_CABLE = register(Constants.NETWORK_CABLE_ITEM_NAME, NetworkCableItem::new);
public static final RegistryObject<NetworkCableItem> NETWORK_CABLE = register(Constants.NETWORK_CABLE_ITEM_NAME, NetworkCableItem::new);
public static final RegistryObject<Item> ROBOT = register(Constants.ROBOT_ENTITY_NAME, RobotItem::new);
public static final RegistryObject<MemoryItem> MEMORY = register(Constants.MEMORY_ITEM_NAME, MemoryItem::new);
@@ -59,6 +60,10 @@ public final class Items {
}
private static <T extends Block> RegistryObject<Item> register(final String name, final RegistryObject<T> block) {
return register(name, () -> new ModBlockItem(block.get()));
return register(name, block, ModBlockItem::new);
}
private static <TBlock extends Block, TItem extends Item> RegistryObject<TItem> register(final String name, final RegistryObject<TBlock> block, final Function<TBlock, TItem> factory) {
return register(name, () -> factory.apply(block.get()));
}
}

View File

@@ -1,40 +1,43 @@
package li.cil.oc2.common.item;
import li.cil.oc2.api.API;
import li.cil.oc2.common.util.WorldUtils;
import li.cil.oc2.common.tags.BlockTags;
import net.minecraft.block.BlockState;
import net.minecraft.block.SoundType;
import net.minecraft.client.Minecraft;
import net.minecraft.entity.player.PlayerEntity;
import net.minecraft.entity.player.ServerPlayerEntity;
import net.minecraft.item.ItemStack;
import net.minecraft.item.ItemUseContext;
import net.minecraft.server.management.PlayerInteractionManager;
import net.minecraft.util.ActionResultType;
import net.minecraft.util.ResourceLocation;
import net.minecraft.util.math.BlockPos;
import net.minecraft.world.IWorldReader;
import net.minecraft.world.World;
import java.util.Objects;
public final class WrenchItem extends ModItem {
@Override
public ActionResultType onItemUse(final ItemUseContext context) {
final World world = context.getWorld();
final BlockPos pos = context.getPos();
final BlockState state = world.getBlockState(pos);
final ResourceLocation registryName = state.getBlock().getRegistryName();
if (registryName == null || !Objects.equals(registryName.getNamespace(), API.MOD_ID)) {
final PlayerEntity player = context.getPlayer();
if (!player.isSneaking()) {
return super.onItemUse(context);
}
final PlayerEntity player = context.getPlayer();
if (player instanceof ServerPlayerEntity) {
final PlayerInteractionManager interactionManager = ((ServerPlayerEntity) player).interactionManager;
if (interactionManager.tryHarvestBlock(pos)) {
WorldUtils.playSound(world, pos, state.getSoundType(), SoundType::getBreakSound);
return ActionResultType.SUCCESS;
}
final World world = context.getWorld();
final BlockPos pos = context.getPos();
final BlockState state = world.getBlockState(pos);
if (!state.isIn(BlockTags.WRENCH_BREAKABLE)) {
return super.onItemUse(context);
}
return super.onItemUse(context);
if (world.isRemote()) {
Minecraft.getInstance().playerController.onPlayerDestroyBlock(pos);
} else if (player instanceof ServerPlayerEntity) {
((ServerPlayerEntity) player).interactionManager.tryHarvestBlock(pos);
}
return ActionResultType.func_233537_a_(world.isRemote);
}
@Override
public boolean doesSneakBypassUse(final ItemStack stack, final IWorldReader world, final BlockPos pos, final PlayerEntity player) {
return true;
}
}

View File

@@ -8,6 +8,7 @@ import net.minecraftforge.common.Tags;
public final class BlockTags {
public static final Tags.IOptionalNamedTag<Block> DEVICES = tag("devices");
public static final Tags.IOptionalNamedTag<Block> CABLES = tag("cables");
public static final Tags.IOptionalNamedTag<Block> WRENCH_BREAKABLE = tag("wrench_breakable");
///////////////////////////////////////////////////////////////////

View File

@@ -36,9 +36,16 @@ public final class BusCableTileEntity extends AbstractTileEntity {
requestModelDataUpdate();
}
public void handleConnectionTypeChanged(final Direction side) {
invalidateCapability(Capabilities.DEVICE_BUS_ELEMENT, side);
handleNeighborChanged(getPos().offset(side));
public void handleConnectivityChanged(@Nullable final Direction side) {
if (side == null) {
busElement.scheduleScan();
// TODO Remove if https://github.com/MinecraftForge/MinecraftForge/pull/7595 gets merged.
requestModelDataUpdate();
} else {
invalidateCapability(Capabilities.DEVICE_BUS_ELEMENT, side);
handleNeighborChanged(getPos().offset(side));
}
}
@Override
@@ -102,14 +109,14 @@ public final class BusCableTileEntity extends AbstractTileEntity {
@Override
public boolean canScanContinueTowards(@Nullable final Direction direction) {
final BusCableBlock.ConnectionType connectionType = BusCableBlock.getConnectionType(getBlockState(), direction);
return connectionType == BusCableBlock.ConnectionType.LINK ||
connectionType == BusCableBlock.ConnectionType.PLUG;
return connectionType == BusCableBlock.ConnectionType.CABLE ||
connectionType == BusCableBlock.ConnectionType.INTERFACE;
}
@Override
public boolean canDetectDevicesTowards(@Nullable final Direction direction) {
final BusCableBlock.ConnectionType connectionType = BusCableBlock.getConnectionType(getBlockState(), direction);
return connectionType == BusCableBlock.ConnectionType.PLUG;
return connectionType == BusCableBlock.ConnectionType.INTERFACE;
}
}
}

View File

@@ -106,11 +106,15 @@ public final class ItemStackUtils {
return Optional.of(entity);
}
public static Optional<ItemEntity> spawnAsEntity(final World world, final BlockPos pos, final ItemStack stack, final Direction direction) {
public static Optional<ItemEntity> spawnAsEntity(final World world, final BlockPos pos, final ItemStack stack, @Nullable final Direction direction) {
return spawnAsEntity(world, Vector3d.copyCentered(pos), stack, direction);
}
public static Optional<ItemEntity> spawnAsEntity(final World world, final Vector3d pos, final ItemStack stack, final Direction direction) {
public static Optional<ItemEntity> spawnAsEntity(final World world, final Vector3d pos, final ItemStack stack, @Nullable final Direction direction) {
if (direction == null) {
return spawnAsEntity(world, pos, stack);
}
if (world.isRemote() || stack.isEmpty()) {
return Optional.empty();
}

View File

@@ -49,7 +49,12 @@ public class ModBlockStateProvider extends BlockStateProvider {
// NB: We use a custom model loader + baked model to replace the base part with straight parts and
// insert supports where appropriate.
builder.part().modelFile(baseModel).addModel().end();
builder.part()
.modelFile(baseModel)
.addModel()
.condition(BusCableBlock.HAS_CABLE, true)
.end();
BusCableBlock.FACING_TO_CONNECTION_MAP.forEach((direction, connectionType) -> {
final int rotationY = (int) direction.getHorizontalAngle();
@@ -67,7 +72,7 @@ public class ModBlockStateProvider extends BlockStateProvider {
.rotationY(rotationY)
.rotationX(rotationX)
.addModel()
.condition(connectionType, BusCableBlock.ConnectionType.LINK)
.condition(connectionType, BusCableBlock.ConnectionType.CABLE)
.end();
builder.part()
@@ -75,7 +80,7 @@ public class ModBlockStateProvider extends BlockStateProvider {
.rotationY(rotationY)
.rotationX(rotationX)
.addModel()
.condition(connectionType, BusCableBlock.ConnectionType.PLUG)
.condition(connectionType, BusCableBlock.ConnectionType.INTERFACE)
.end();
});

View File

@@ -7,8 +7,7 @@ import net.minecraftforge.common.data.ExistingFileHelper;
import org.jetbrains.annotations.Nullable;
import static li.cil.oc2.common.block.Blocks.*;
import static li.cil.oc2.common.tags.BlockTags.CABLES;
import static li.cil.oc2.common.tags.BlockTags.DEVICES;
import static li.cil.oc2.common.tags.BlockTags.*;
public final class ModBlockTagsProvider extends BlockTagsProvider {
public ModBlockTagsProvider(final DataGenerator generatorIn, @Nullable final ExistingFileHelper existingFileHelper) {
@@ -22,6 +21,16 @@ public final class ModBlockTagsProvider extends BlockTagsProvider {
REDSTONE_INTERFACE.get(),
DISK_DRIVE.get()
);
getOrCreateBuilder(CABLES).add(BUS_CABLE.get());
getOrCreateBuilder(CABLES).add(
BUS_CABLE.get()
);
getOrCreateBuilder(WRENCH_BREAKABLE).add(
COMPUTER.get(),
BUS_CABLE.get(),
NETWORK_CONNECTOR.get(),
NETWORK_HUB.get(),
REDSTONE_INTERFACE.get(),
DISK_DRIVE.get()
);
}
}

View File

@@ -42,7 +42,6 @@ public final class ModLootTableProvider extends LootTableProvider {
public static final class ModBlockLootTables extends BlockLootTables {
@Override
protected void addTables() {
registerDropSelfLootTable(Blocks.BUS_CABLE.get());
registerDropSelfLootTable(Blocks.REDSTONE_INTERFACE.get());
registerDropSelfLootTable(Blocks.NETWORK_CONNECTOR.get());
registerDropSelfLootTable(Blocks.NETWORK_HUB.get());
@@ -55,6 +54,7 @@ public final class ModLootTableProvider extends LootTableProvider {
protected Iterable<Block> getKnownBlocks() {
return StreamSupport.stream(super.getKnownBlocks().spliterator(), false)
.filter(block -> requireNonNull(block.getRegistryName()).getNamespace().equals(API.MOD_ID))
.filter(block -> block != Blocks.BUS_CABLE.get()) // All bus drops depend on block state.
.collect(Collectors.toList());
}

View File

@@ -1,13 +1,16 @@
{
"multipart": [
{
"when": {
"has_cable": "true"
},
"apply": {
"model": "oc2:block/cable_base"
}
},
{
"when": {
"connection_down": "link"
"connection_down": "cable"
},
"apply": {
"model": "oc2:block/cable_link",
@@ -17,7 +20,7 @@
},
{
"when": {
"connection_down": "plug"
"connection_down": "interface"
},
"apply": {
"model": "oc2:block/cable_plug",
@@ -27,7 +30,7 @@
},
{
"when": {
"connection_up": "link"
"connection_up": "cable"
},
"apply": {
"model": "oc2:block/cable_link",
@@ -37,7 +40,7 @@
},
{
"when": {
"connection_up": "plug"
"connection_up": "interface"
},
"apply": {
"model": "oc2:block/cable_plug",
@@ -47,7 +50,7 @@
},
{
"when": {
"connection_north": "link"
"connection_north": "cable"
},
"apply": {
"model": "oc2:block/cable_link",
@@ -56,7 +59,7 @@
},
{
"when": {
"connection_north": "plug"
"connection_north": "interface"
},
"apply": {
"model": "oc2:block/cable_plug",
@@ -65,7 +68,7 @@
},
{
"when": {
"connection_south": "link"
"connection_south": "cable"
},
"apply": {
"model": "oc2:block/cable_link"
@@ -73,7 +76,7 @@
},
{
"when": {
"connection_south": "plug"
"connection_south": "interface"
},
"apply": {
"model": "oc2:block/cable_plug"
@@ -81,7 +84,7 @@
},
{
"when": {
"connection_west": "link"
"connection_west": "cable"
},
"apply": {
"model": "oc2:block/cable_link",
@@ -90,7 +93,7 @@
},
{
"when": {
"connection_west": "plug"
"connection_west": "interface"
},
"apply": {
"model": "oc2:block/cable_plug",
@@ -99,7 +102,7 @@
},
{
"when": {
"connection_east": "link"
"connection_east": "cable"
},
"apply": {
"model": "oc2:block/cable_link",
@@ -108,7 +111,7 @@
},
{
"when": {
"connection_east": "plug"
"connection_east": "interface"
},
"apply": {
"model": "oc2:block/cable_plug",

View File

@@ -1,19 +0,0 @@
{
"type": "minecraft:block",
"pools": [
{
"rolls": 1,
"entries": [
{
"type": "minecraft:item",
"name": "oc2:bus_cable"
}
],
"conditions": [
{
"condition": "minecraft:survives_explosion"
}
]
}
]
}

View File

@@ -0,0 +1,11 @@
{
"replace": false,
"values": [
"oc2:computer",
"oc2:bus_cable",
"oc2:network_connector",
"oc2:network_hub",
"oc2:redstone_interface",
"oc2:disk_drive"
]
}