CC-Tweaked/projects/common/src/main/java/dan200/computercraft/shared/turtle/blocks/TurtleBlock.java

225 lines
9.4 KiB
Java

// Copyright Daniel Ratcliffe, 2011-2022. Do not distribute without permission.
//
// SPDX-License-Identifier: LicenseRef-CCPL
package dan200.computercraft.shared.turtle.blocks;
import com.mojang.serialization.MapCodec;
import com.mojang.serialization.codecs.RecordCodecBuilder;
import dan200.computercraft.annotations.ForgeOverride;
import dan200.computercraft.api.turtle.ITurtleUpgrade;
import dan200.computercraft.api.turtle.TurtleSide;
import dan200.computercraft.api.upgrades.UpgradeData;
import dan200.computercraft.shared.computer.blocks.AbstractComputerBlock;
import dan200.computercraft.shared.computer.blocks.AbstractComputerBlockEntity;
import dan200.computercraft.shared.platform.RegistryEntry;
import dan200.computercraft.shared.turtle.core.TurtleBrain;
import dan200.computercraft.shared.turtle.items.TurtleItem;
import dan200.computercraft.shared.util.BlockCodecs;
import dan200.computercraft.shared.util.BlockEntityHelpers;
import dan200.computercraft.shared.util.WaterloggableHelpers;
import net.minecraft.core.BlockPos;
import net.minecraft.core.Direction;
import net.minecraft.world.Containers;
import net.minecraft.world.InteractionHand;
import net.minecraft.world.InteractionResult;
import net.minecraft.world.entity.LivingEntity;
import net.minecraft.world.entity.player.Player;
import net.minecraft.world.entity.projectile.AbstractHurtingProjectile;
import net.minecraft.world.item.ItemStack;
import net.minecraft.world.item.Items;
import net.minecraft.world.item.context.BlockPlaceContext;
import net.minecraft.world.level.BlockGetter;
import net.minecraft.world.level.Explosion;
import net.minecraft.world.level.Level;
import net.minecraft.world.level.LevelAccessor;
import net.minecraft.world.level.block.Block;
import net.minecraft.world.level.block.RenderShape;
import net.minecraft.world.level.block.SimpleWaterloggedBlock;
import net.minecraft.world.level.block.entity.BlockEntity;
import net.minecraft.world.level.block.entity.BlockEntityTicker;
import net.minecraft.world.level.block.entity.BlockEntityType;
import net.minecraft.world.level.block.state.BlockState;
import net.minecraft.world.level.block.state.StateDefinition;
import net.minecraft.world.level.block.state.properties.BlockStateProperties;
import net.minecraft.world.level.block.state.properties.DirectionProperty;
import net.minecraft.world.level.material.FluidState;
import net.minecraft.world.phys.BlockHitResult;
import net.minecraft.world.phys.Vec3;
import net.minecraft.world.phys.shapes.CollisionContext;
import net.minecraft.world.phys.shapes.Shapes;
import net.minecraft.world.phys.shapes.VoxelShape;
import javax.annotation.Nullable;
import static dan200.computercraft.shared.util.WaterloggableHelpers.WATERLOGGED;
import static dan200.computercraft.shared.util.WaterloggableHelpers.getFluidStateForPlacement;
public class TurtleBlock extends AbstractComputerBlock<TurtleBlockEntity> implements SimpleWaterloggedBlock {
private static final MapCodec<TurtleBlock> CODEC = RecordCodecBuilder.mapCodec(instance -> instance.group(
BlockCodecs.propertiesCodec(),
BlockCodecs.blockEntityCodec(x -> x.type)
).apply(instance, TurtleBlock::new));
public static final DirectionProperty FACING = BlockStateProperties.HORIZONTAL_FACING;
/**
* The explosion resistance to use when a turtle is "immune" to explosions.
* <p>
* This is used as the default explosion resistance for advanced turtles, and the resistance for entity-based
* explosions (e.g. creepers).
*
* @see #getExplosionResistance(BlockState, BlockGetter, BlockPos, Explosion)
*/
public static final float IMMUNE_EXPLOSION_RESISTANCE = 2000f;
private static final VoxelShape DEFAULT_SHAPE = Shapes.box(
0.125, 0.125, 0.125,
0.875, 0.875, 0.875
);
private final BlockEntityTicker<TurtleBlockEntity> clientTicker = (level, pos, state, computer) -> computer.clientTick();
public TurtleBlock(Properties settings, RegistryEntry<BlockEntityType<TurtleBlockEntity>> type) {
super(settings, type);
registerDefaultState(getStateDefinition().any()
.setValue(FACING, Direction.NORTH)
.setValue(WATERLOGGED, false)
);
}
@Override
protected void createBlockStateDefinition(StateDefinition.Builder<Block, BlockState> builder) {
builder.add(FACING, WATERLOGGED);
}
@Override
protected MapCodec<? extends TurtleBlock> codec() {
return CODEC;
}
@Override
@Deprecated
public RenderShape getRenderShape(BlockState state) {
return RenderShape.ENTITYBLOCK_ANIMATED;
}
@Override
@Deprecated
public VoxelShape getShape(BlockState state, BlockGetter world, BlockPos pos, CollisionContext context) {
var tile = world.getBlockEntity(pos);
var offset = tile instanceof TurtleBlockEntity turtle ? turtle.getRenderOffset(1.0f) : Vec3.ZERO;
return offset.equals(Vec3.ZERO) ? DEFAULT_SHAPE : DEFAULT_SHAPE.move(offset.x, offset.y, offset.z);
}
@Nullable
@Override
public BlockState getStateForPlacement(BlockPlaceContext placement) {
return defaultBlockState()
.setValue(FACING, placement.getHorizontalDirection())
.setValue(WATERLOGGED, getFluidStateForPlacement(placement));
}
@Override
@Deprecated
public FluidState getFluidState(BlockState state) {
return WaterloggableHelpers.getFluidState(state);
}
@Override
@Deprecated
public BlockState updateShape(BlockState state, Direction side, BlockState otherState, LevelAccessor world, BlockPos pos, BlockPos otherPos) {
WaterloggableHelpers.updateShape(state, world, pos);
return state;
}
@Override
@Deprecated
public final void onRemove(BlockState state, Level level, BlockPos pos, BlockState newState, boolean isMoving) {
if (state.is(newState.getBlock())) return;
if (!level.isClientSide && level.getBlockEntity(pos) instanceof TurtleBlockEntity turtle && !turtle.hasMoved()) {
Containers.dropContents(level, pos, turtle);
}
super.onRemove(state, level, pos, newState, isMoving);
}
@Override
public void setPlacedBy(Level world, BlockPos pos, BlockState state, @Nullable LivingEntity entity, ItemStack stack) {
super.setPlacedBy(world, pos, state, entity, stack);
var tile = world.getBlockEntity(pos);
if (!world.isClientSide && tile instanceof TurtleBlockEntity turtle) {
if (entity instanceof Player player) turtle.setOwningPlayer(player.getGameProfile());
if (stack.getItem() instanceof TurtleItem item) {
// Set Upgrades
for (var side : TurtleSide.values()) {
turtle.getAccess().setUpgradeWithData(side, item.getUpgradeWithData(stack, side));
}
turtle.getAccess().setFuelLevel(item.getFuelLevel(stack));
// Set colour
var colour = item.getColour(stack);
if (colour != -1) turtle.getAccess().setColour(colour);
// Set overlay
var overlay = item.getOverlay(stack);
if (overlay != null) ((TurtleBrain) turtle.getAccess()).setOverlay(overlay);
}
}
}
@Override
@Deprecated
public InteractionResult use(BlockState state, Level level, BlockPos pos, Player player, InteractionHand hand, BlockHitResult hit) {
var currentItem = player.getItemInHand(hand);
if (currentItem.getItem() == Items.NAME_TAG && currentItem.hasCustomHoverName() && level.getBlockEntity(pos) instanceof AbstractComputerBlockEntity computer) {
// Label to rename computer
if (!level.isClientSide) {
computer.setLabel(currentItem.getHoverName().getString());
currentItem.shrink(1);
}
return InteractionResult.sidedSuccess(level.isClientSide);
}
return super.use(state, level, pos, player, hand, hit);
}
@ForgeOverride
public float getExplosionResistance(BlockState state, BlockGetter world, BlockPos pos, Explosion explosion) {
var exploder = explosion.getDirectSourceEntity();
if (exploder instanceof LivingEntity || exploder instanceof AbstractHurtingProjectile) {
return IMMUNE_EXPLOSION_RESISTANCE;
}
return getExplosionResistance();
}
@Override
protected ItemStack getItem(AbstractComputerBlockEntity tile) {
if (!(tile instanceof TurtleBlockEntity turtle)) return ItemStack.EMPTY;
if (!(asItem() instanceof TurtleItem item)) return ItemStack.EMPTY;
var access = turtle.getAccess();
return item.create(
turtle.getComputerID(), turtle.getLabel(), access.getColour(),
withPersistedData(access.getUpgradeWithData(TurtleSide.LEFT)),
withPersistedData(access.getUpgradeWithData(TurtleSide.RIGHT)),
access.getFuelLevel(), turtle.getOverlay()
);
}
private static @Nullable UpgradeData<ITurtleUpgrade> withPersistedData(@Nullable UpgradeData<ITurtleUpgrade> upgrade) {
return upgrade == null ? null : UpgradeData.of(upgrade.upgrade(), upgrade.upgrade().getPersistedData(upgrade.data()));
}
@Override
@Nullable
public <U extends BlockEntity> BlockEntityTicker<U> getTicker(Level level, BlockState state, BlockEntityType<U> type) {
return level.isClientSide ? BlockEntityHelpers.createTickerHelper(type, this.type.get(), clientTicker) : super.getTicker(level, state, type);
}
}