mirror of
https://github.com/SquidDev-CC/CC-Tweaked
synced 2025-11-21 15:54:47 +00:00
570 lines
15 KiB
Java
570 lines
15 KiB
Java
/*
|
|
* This file is part of ComputerCraft - http://www.computercraft.info
|
|
* Copyright Daniel Ratcliffe, 2011-2021. Do not distribute without permission.
|
|
* Send enquiries to dratcliffe@gmail.com
|
|
*/
|
|
|
|
package dan200.computercraft.shared.turtle.blocks;
|
|
|
|
import com.mojang.authlib.GameProfile;
|
|
import dan200.computercraft.ComputerCraft;
|
|
import dan200.computercraft.api.turtle.ITurtleAccess;
|
|
import dan200.computercraft.api.turtle.ITurtleUpgrade;
|
|
import dan200.computercraft.api.turtle.TurtleSide;
|
|
import dan200.computercraft.core.computer.ComputerSide;
|
|
import dan200.computercraft.shared.common.TileGeneric;
|
|
import dan200.computercraft.shared.computer.blocks.ComputerProxy;
|
|
import dan200.computercraft.shared.computer.blocks.TileComputerBase;
|
|
import dan200.computercraft.shared.computer.core.ComputerFamily;
|
|
import dan200.computercraft.shared.computer.core.ComputerState;
|
|
import dan200.computercraft.shared.computer.core.ServerComputer;
|
|
import dan200.computercraft.shared.turtle.apis.TurtleAPI;
|
|
import dan200.computercraft.shared.turtle.blocks.TileTurtle.MoveState;
|
|
import dan200.computercraft.shared.turtle.core.TurtleBrain;
|
|
import dan200.computercraft.shared.turtle.inventory.ContainerTurtle;
|
|
import dan200.computercraft.shared.util.*;
|
|
import net.minecraft.block.BlockState;
|
|
import net.minecraft.block.entity.BlockEntityType;
|
|
import net.minecraft.entity.player.PlayerEntity;
|
|
import net.minecraft.entity.player.PlayerInventory;
|
|
import net.minecraft.item.DyeItem;
|
|
import net.minecraft.item.ItemStack;
|
|
import net.minecraft.item.Items;
|
|
import net.minecraft.nbt.NbtCompound;
|
|
import net.minecraft.nbt.NbtList;
|
|
import net.minecraft.screen.ScreenHandler;
|
|
import net.minecraft.util.ActionResult;
|
|
import net.minecraft.util.DyeColor;
|
|
import net.minecraft.util.Hand;
|
|
import net.minecraft.util.Identifier;
|
|
import net.minecraft.util.collection.DefaultedList;
|
|
import net.minecraft.util.hit.BlockHitResult;
|
|
import net.minecraft.util.math.BlockPos;
|
|
import net.minecraft.util.math.Direction;
|
|
import net.minecraft.util.math.Vec3d;
|
|
|
|
import javax.annotation.Nonnull;
|
|
import javax.annotation.Nullable;
|
|
import java.util.Collections;
|
|
|
|
public class TileTurtle extends TileComputerBase implements ITurtleTile, DefaultInventory
|
|
{
|
|
public static final int INVENTORY_SIZE = 16;
|
|
public static final int INVENTORY_WIDTH = 4;
|
|
public static final int INVENTORY_HEIGHT = 4;
|
|
private final DefaultedList<ItemStack> inventory = DefaultedList.ofSize( INVENTORY_SIZE, ItemStack.EMPTY );
|
|
private final DefaultedList<ItemStack> previousInventory = DefaultedList.ofSize( INVENTORY_SIZE, ItemStack.EMPTY );
|
|
private boolean inventoryChanged = false;
|
|
private TurtleBrain brain = new TurtleBrain( this );
|
|
private MoveState moveState = MoveState.NOT_MOVED;
|
|
|
|
public TileTurtle( BlockEntityType<? extends TileGeneric> type, ComputerFamily family )
|
|
{
|
|
super( type, family );
|
|
}
|
|
|
|
@Override
|
|
protected void unload()
|
|
{
|
|
if( !hasMoved() )
|
|
{
|
|
super.unload();
|
|
}
|
|
}
|
|
|
|
@Override
|
|
public void destroy()
|
|
{
|
|
if( !hasMoved() )
|
|
{
|
|
// Stop computer
|
|
super.destroy();
|
|
|
|
// Drop contents
|
|
if( !getWorld().isClient )
|
|
{
|
|
int size = size();
|
|
for( int i = 0; i < size; i++ )
|
|
{
|
|
ItemStack stack = getStack( i );
|
|
if( !stack.isEmpty() )
|
|
{
|
|
WorldUtil.dropItemStack( stack, getWorld(), getPos() );
|
|
}
|
|
}
|
|
}
|
|
}
|
|
else
|
|
{
|
|
// Just turn off any redstone we had on
|
|
for( Direction dir : DirectionUtil.FACINGS )
|
|
{
|
|
RedstoneUtil.propagateRedstoneOutput( getWorld(), getPos(), dir );
|
|
}
|
|
}
|
|
}
|
|
|
|
private boolean hasMoved()
|
|
{
|
|
return moveState == MoveState.MOVED;
|
|
}
|
|
|
|
@Override
|
|
public int size()
|
|
{
|
|
return INVENTORY_SIZE;
|
|
}
|
|
|
|
@Override
|
|
public boolean isEmpty()
|
|
{
|
|
for( ItemStack stack : inventory )
|
|
{
|
|
if( !stack.isEmpty() )
|
|
{
|
|
return false;
|
|
}
|
|
}
|
|
return true;
|
|
}
|
|
|
|
@Nonnull
|
|
@Override
|
|
public ItemStack getStack( int slot )
|
|
{
|
|
return slot >= 0 && slot < INVENTORY_SIZE ? inventory.get( slot ) : ItemStack.EMPTY;
|
|
}
|
|
|
|
@Nonnull
|
|
@Override
|
|
public ItemStack removeStack( int slot, int count )
|
|
{
|
|
if( count == 0 )
|
|
{
|
|
return ItemStack.EMPTY;
|
|
}
|
|
|
|
ItemStack stack = getStack( slot );
|
|
if( stack.isEmpty() )
|
|
{
|
|
return ItemStack.EMPTY;
|
|
}
|
|
|
|
if( stack.getCount() <= count )
|
|
{
|
|
setStack( slot, ItemStack.EMPTY );
|
|
return stack;
|
|
}
|
|
|
|
ItemStack part = stack.split( count );
|
|
onInventoryDefinitelyChanged();
|
|
return part;
|
|
}
|
|
|
|
@Nonnull
|
|
@Override
|
|
public ItemStack removeStack( int slot )
|
|
{
|
|
ItemStack result = getStack( slot );
|
|
setStack( slot, ItemStack.EMPTY );
|
|
return result;
|
|
}
|
|
|
|
@Override
|
|
public void setStack( int i, @Nonnull ItemStack stack )
|
|
{
|
|
if( i >= 0 && i < INVENTORY_SIZE && !InventoryUtil.areItemsEqual( stack, inventory.get( i ) ) )
|
|
{
|
|
inventory.set( i, stack );
|
|
onInventoryDefinitelyChanged();
|
|
}
|
|
}
|
|
|
|
@Override
|
|
public boolean canPlayerUse( @Nonnull PlayerEntity player )
|
|
{
|
|
return isUsable( player, false );
|
|
}
|
|
|
|
private void onInventoryDefinitelyChanged()
|
|
{
|
|
super.markDirty();
|
|
inventoryChanged = true;
|
|
}
|
|
|
|
@Override
|
|
protected boolean canNameWithTag( PlayerEntity player )
|
|
{
|
|
return true;
|
|
}
|
|
|
|
@Nonnull
|
|
@Override
|
|
public ActionResult onActivate( PlayerEntity player, Hand hand, BlockHitResult hit )
|
|
{
|
|
// Apply dye
|
|
ItemStack currentItem = player.getStackInHand( hand );
|
|
if( !currentItem.isEmpty() )
|
|
{
|
|
if( currentItem.getItem() instanceof DyeItem )
|
|
{
|
|
// Dye to change turtle colour
|
|
if( !getWorld().isClient )
|
|
{
|
|
DyeColor dye = ((DyeItem) currentItem.getItem()).getColor();
|
|
if( brain.getDyeColour() != dye )
|
|
{
|
|
brain.setDyeColour( dye );
|
|
if( !player.isCreative() )
|
|
{
|
|
currentItem.decrement( 1 );
|
|
}
|
|
}
|
|
}
|
|
return ActionResult.SUCCESS;
|
|
}
|
|
else if( currentItem.getItem() == Items.WATER_BUCKET && brain.getColour() != -1 )
|
|
{
|
|
// Water to remove turtle colour
|
|
if( !getWorld().isClient )
|
|
{
|
|
if( brain.getColour() != -1 )
|
|
{
|
|
brain.setColour( -1 );
|
|
if( !player.isCreative() )
|
|
{
|
|
player.setStackInHand( hand, new ItemStack( Items.BUCKET ) );
|
|
player.inventory.markDirty();
|
|
}
|
|
}
|
|
}
|
|
return ActionResult.SUCCESS;
|
|
}
|
|
}
|
|
|
|
// Open GUI or whatever
|
|
return super.onActivate( player, hand, hit );
|
|
}
|
|
|
|
@Override
|
|
public void onNeighbourChange( @Nonnull BlockPos neighbour )
|
|
{
|
|
if( moveState == MoveState.NOT_MOVED )
|
|
{
|
|
super.onNeighbourChange( neighbour );
|
|
}
|
|
}
|
|
|
|
@Override
|
|
public void onNeighbourTileEntityChange( @Nonnull BlockPos neighbour )
|
|
{
|
|
if( moveState == MoveState.NOT_MOVED )
|
|
{
|
|
super.onNeighbourTileEntityChange( neighbour );
|
|
}
|
|
}
|
|
|
|
@Override
|
|
public void tick()
|
|
{
|
|
super.tick();
|
|
brain.update();
|
|
if( !getWorld().isClient && inventoryChanged )
|
|
{
|
|
ServerComputer computer = getServerComputer();
|
|
if( computer != null )
|
|
{
|
|
computer.queueEvent( "turtle_inventory" );
|
|
}
|
|
|
|
inventoryChanged = false;
|
|
for( int n = 0; n < size(); n++ )
|
|
{
|
|
previousInventory.set( n,
|
|
getStack( n ).copy() );
|
|
}
|
|
}
|
|
}
|
|
|
|
@Override
|
|
protected void updateBlockState( ComputerState newState )
|
|
{
|
|
}
|
|
|
|
@Nonnull
|
|
@Override
|
|
public NbtCompound writeNbt( @Nonnull NbtCompound nbt )
|
|
{
|
|
// Write inventory
|
|
NbtList nbttaglist = new NbtList();
|
|
for( int i = 0; i < INVENTORY_SIZE; i++ )
|
|
{
|
|
if( !inventory.get( i )
|
|
.isEmpty() )
|
|
{
|
|
NbtCompound tag = new NbtCompound();
|
|
tag.putByte( "Slot", (byte) i );
|
|
inventory.get( i )
|
|
.writeNbt( tag );
|
|
nbttaglist.add( tag );
|
|
}
|
|
}
|
|
nbt.put( "Items", nbttaglist );
|
|
|
|
// Write brain
|
|
nbt = brain.writeToNBT( nbt );
|
|
|
|
return super.writeNbt( nbt );
|
|
}
|
|
|
|
// IDirectionalTile
|
|
|
|
@Override
|
|
public void readNbt( @Nonnull BlockState state, @Nonnull NbtCompound nbt )
|
|
{
|
|
super.readNbt( state, nbt );
|
|
|
|
// Read inventory
|
|
NbtList nbttaglist = nbt.getList( "Items", NBTUtil.TAG_COMPOUND );
|
|
inventory.clear();
|
|
previousInventory.clear();
|
|
for( int i = 0; i < nbttaglist.size(); i++ )
|
|
{
|
|
NbtCompound tag = nbttaglist.getCompound( i );
|
|
int slot = tag.getByte( "Slot" ) & 0xff;
|
|
if( slot < size() )
|
|
{
|
|
inventory.set( slot, ItemStack.fromNbt( tag ) );
|
|
previousInventory.set( slot, inventory.get( slot )
|
|
.copy() );
|
|
}
|
|
}
|
|
|
|
// Read state
|
|
brain.readFromNBT( nbt );
|
|
}
|
|
|
|
@Override
|
|
protected boolean isPeripheralBlockedOnSide( ComputerSide localSide )
|
|
{
|
|
return hasPeripheralUpgradeOnSide( localSide );
|
|
}
|
|
|
|
// ITurtleTile
|
|
|
|
@Override
|
|
public Direction getDirection()
|
|
{
|
|
return getCachedState().get( BlockTurtle.FACING );
|
|
}
|
|
|
|
@Override
|
|
protected ServerComputer createComputer( int instanceID, int id )
|
|
{
|
|
ServerComputer computer = new ServerComputer( getWorld(),
|
|
id, label,
|
|
instanceID, getFamily(),
|
|
ComputerCraft.turtleTermWidth,
|
|
ComputerCraft.turtleTermHeight );
|
|
computer.setPosition( getPos() );
|
|
computer.addAPI( new TurtleAPI( computer.getAPIEnvironment(), getAccess() ) );
|
|
brain.setupComputer( computer );
|
|
return computer;
|
|
}
|
|
|
|
@Override
|
|
protected void writeDescription( @Nonnull NbtCompound nbt )
|
|
{
|
|
super.writeDescription( nbt );
|
|
brain.writeDescription( nbt );
|
|
}
|
|
|
|
@Override
|
|
protected void readDescription( @Nonnull NbtCompound nbt )
|
|
{
|
|
super.readDescription( nbt );
|
|
brain.readDescription( nbt );
|
|
}
|
|
|
|
@Override
|
|
public ComputerProxy createProxy()
|
|
{
|
|
return brain.getProxy();
|
|
}
|
|
|
|
public void setDirection( Direction dir )
|
|
{
|
|
if( dir.getAxis() == Direction.Axis.Y )
|
|
{
|
|
dir = Direction.NORTH;
|
|
}
|
|
world.setBlockState( pos,
|
|
getCachedState().with( BlockTurtle.FACING, dir ) );
|
|
updateOutput();
|
|
updateInput();
|
|
onTileEntityChange();
|
|
}
|
|
|
|
public void onTileEntityChange()
|
|
{
|
|
super.markDirty();
|
|
}
|
|
|
|
private boolean hasPeripheralUpgradeOnSide( ComputerSide side )
|
|
{
|
|
ITurtleUpgrade upgrade;
|
|
switch( side )
|
|
{
|
|
case RIGHT:
|
|
upgrade = getUpgrade( TurtleSide.RIGHT );
|
|
break;
|
|
case LEFT:
|
|
upgrade = getUpgrade( TurtleSide.LEFT );
|
|
break;
|
|
default:
|
|
return false;
|
|
}
|
|
return upgrade != null && upgrade.getType()
|
|
.isPeripheral();
|
|
}
|
|
|
|
// IInventory
|
|
|
|
@Override
|
|
protected double getInteractRange( PlayerEntity player )
|
|
{
|
|
return 12.0;
|
|
}
|
|
|
|
public void notifyMoveStart()
|
|
{
|
|
if( moveState == MoveState.NOT_MOVED )
|
|
{
|
|
moveState = MoveState.IN_PROGRESS;
|
|
}
|
|
}
|
|
|
|
public void notifyMoveEnd()
|
|
{
|
|
// MoveState.MOVED is final
|
|
if( moveState == MoveState.IN_PROGRESS )
|
|
{
|
|
moveState = MoveState.NOT_MOVED;
|
|
}
|
|
}
|
|
|
|
@Override
|
|
public int getColour()
|
|
{
|
|
return brain.getColour();
|
|
}
|
|
|
|
@Override
|
|
public Identifier getOverlay()
|
|
{
|
|
return brain.getOverlay();
|
|
}
|
|
|
|
@Override
|
|
public ITurtleUpgrade getUpgrade( TurtleSide side )
|
|
{
|
|
return brain.getUpgrade( side );
|
|
}
|
|
|
|
@Override
|
|
public ITurtleAccess getAccess()
|
|
{
|
|
return brain;
|
|
}
|
|
|
|
@Override
|
|
public Vec3d getRenderOffset( float f )
|
|
{
|
|
return brain.getRenderOffset( f );
|
|
}
|
|
|
|
@Override
|
|
public float getRenderYaw( float f )
|
|
{
|
|
return brain.getVisualYaw( f );
|
|
}
|
|
|
|
@Override
|
|
public float getToolRenderAngle( TurtleSide side, float f )
|
|
{
|
|
return brain.getToolRenderAngle( side, f );
|
|
}
|
|
|
|
void setOwningPlayer( GameProfile player )
|
|
{
|
|
brain.setOwningPlayer( player );
|
|
markDirty();
|
|
}
|
|
|
|
// Networking stuff
|
|
|
|
@Override
|
|
public void markDirty()
|
|
{
|
|
super.markDirty();
|
|
if( !inventoryChanged )
|
|
{
|
|
for( int n = 0; n < size(); n++ )
|
|
{
|
|
if( !ItemStack.areEqual( getStack( n ), previousInventory.get( n ) ) )
|
|
{
|
|
inventoryChanged = true;
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
@Override
|
|
public void clear()
|
|
{
|
|
boolean changed = false;
|
|
for( int i = 0; i < INVENTORY_SIZE; i++ )
|
|
{
|
|
if( !inventory.get( i )
|
|
.isEmpty() )
|
|
{
|
|
inventory.set( i, ItemStack.EMPTY );
|
|
changed = true;
|
|
}
|
|
}
|
|
|
|
if( changed )
|
|
{
|
|
onInventoryDefinitelyChanged();
|
|
}
|
|
}
|
|
|
|
// Privates
|
|
|
|
public void transferStateFrom( TileTurtle copy )
|
|
{
|
|
super.transferStateFrom( copy );
|
|
Collections.copy( inventory, copy.inventory );
|
|
Collections.copy( previousInventory, copy.previousInventory );
|
|
inventoryChanged = copy.inventoryChanged;
|
|
brain = copy.brain;
|
|
brain.setOwner( this );
|
|
|
|
// Mark the other turtle as having moved, and so its peripheral is dead.
|
|
copy.moveState = MoveState.MOVED;
|
|
}
|
|
|
|
@Nullable
|
|
@Override
|
|
public ScreenHandler createMenu( int id, @Nonnull PlayerInventory inventory, @Nonnull PlayerEntity player )
|
|
{
|
|
return new ContainerTurtle( id, inventory, brain );
|
|
}
|
|
|
|
enum MoveState
|
|
{
|
|
NOT_MOVED, IN_PROGRESS, MOVED
|
|
}
|
|
}
|