1182 lines
36 KiB
Java
1182 lines
36 KiB
Java
/**
|
|
* This file is part of ComputerCraft - http://www.computercraft.info
|
|
* Copyright Daniel Ratcliffe, 2011-2016. Do not distribute without permission.
|
|
* Send enquiries to dratcliffe@gmail.com
|
|
*/
|
|
|
|
package dan200.computercraft.shared.turtle.core;
|
|
|
|
import com.google.common.base.Objects;
|
|
import dan200.computercraft.ComputerCraft;
|
|
import dan200.computercraft.api.lua.ILuaContext;
|
|
import dan200.computercraft.api.lua.LuaException;
|
|
import dan200.computercraft.api.peripheral.IPeripheral;
|
|
import dan200.computercraft.api.turtle.*;
|
|
import dan200.computercraft.shared.computer.core.ComputerFamily;
|
|
import dan200.computercraft.shared.computer.core.IComputer;
|
|
import dan200.computercraft.shared.computer.core.ServerComputer;
|
|
import dan200.computercraft.shared.turtle.blocks.TileTurtle;
|
|
import dan200.computercraft.shared.util.Colour;
|
|
import dan200.computercraft.shared.util.DirectionUtil;
|
|
import dan200.computercraft.shared.util.Holiday;
|
|
import dan200.computercraft.shared.util.HolidayUtil;
|
|
import net.minecraft.block.Block;
|
|
import net.minecraft.block.state.IBlockState;
|
|
import net.minecraft.entity.Entity;
|
|
import net.minecraft.inventory.IInventory;
|
|
import net.minecraft.nbt.NBTTagCompound;
|
|
import net.minecraft.tileentity.TileEntity;
|
|
import net.minecraft.util.*;
|
|
import net.minecraft.util.math.*;
|
|
import net.minecraft.util.text.*;
|
|
import net.minecraft.world.World;
|
|
import net.minecraftforge.common.util.Constants;
|
|
|
|
import java.lang.ref.WeakReference;
|
|
import java.util.*;
|
|
|
|
public class TurtleBrain implements ITurtleAccess
|
|
{
|
|
private static int s_nextInstanceID = 0;
|
|
private static Map<Integer, WeakReference<TurtleBrain>> s_allClientBrains = new HashMap<Integer, WeakReference<TurtleBrain>>();
|
|
|
|
public static int assignInstanceID()
|
|
{
|
|
return s_nextInstanceID++;
|
|
}
|
|
|
|
public static TurtleBrain getClientBrain( int instanceID )
|
|
{
|
|
if( instanceID >= 0 )
|
|
{
|
|
WeakReference<TurtleBrain> ref = s_allClientBrains.get( instanceID );
|
|
if( ref != null )
|
|
{
|
|
TurtleBrain brain = ref.get();
|
|
if( brain != null )
|
|
{
|
|
return brain;
|
|
}
|
|
else
|
|
{
|
|
s_allClientBrains.remove( instanceID );
|
|
}
|
|
}
|
|
}
|
|
return null;
|
|
}
|
|
|
|
public static void setClientBrain( int instanceID, TurtleBrain brain )
|
|
{
|
|
if( instanceID >= 0 )
|
|
{
|
|
if( getClientBrain( instanceID ) != brain )
|
|
{
|
|
s_allClientBrains.put( instanceID, new WeakReference<TurtleBrain>( brain ) );
|
|
}
|
|
}
|
|
}
|
|
|
|
public static void cleanupBrains()
|
|
{
|
|
if( s_allClientBrains.size() > 0 )
|
|
{
|
|
Iterator<Map.Entry<Integer, WeakReference<TurtleBrain>>> it = s_allClientBrains.entrySet().iterator();
|
|
while( it.hasNext() )
|
|
{
|
|
Map.Entry<Integer, WeakReference<TurtleBrain>> entry = it.next();
|
|
WeakReference<TurtleBrain> ref = entry.getValue();
|
|
if( ref != null )
|
|
{
|
|
TurtleBrain brain = ref.get();
|
|
if( brain == null )
|
|
{
|
|
it.remove();
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
private static final int ANIM_DURATION = 8;
|
|
|
|
private TileTurtle m_owner;
|
|
|
|
private LinkedList<TurtleCommandQueueEntry> m_commandQueue;
|
|
private int m_commandsIssued;
|
|
|
|
private Map<TurtleSide, ITurtleUpgrade> m_upgrades;
|
|
private Map<TurtleSide, IPeripheral> m_peripherals;
|
|
private Map<TurtleSide, NBTTagCompound> m_upgradeNBTData;
|
|
|
|
private int m_selectedSlot;
|
|
private int m_fuelLevel;
|
|
private Colour m_colour;
|
|
private ResourceLocation m_overlay;
|
|
|
|
private int m_instanceID;
|
|
private EnumFacing m_direction;
|
|
private TurtleAnimation m_animation;
|
|
private int m_animationProgress;
|
|
private int m_lastAnimationProgress;
|
|
|
|
public TurtleBrain( TileTurtle turtle )
|
|
{
|
|
m_owner = turtle;
|
|
|
|
m_commandQueue = new LinkedList<TurtleCommandQueueEntry>();
|
|
m_commandsIssued = 0;
|
|
|
|
m_upgrades = new HashMap<TurtleSide, ITurtleUpgrade>();
|
|
m_peripherals = new HashMap<TurtleSide, IPeripheral>();
|
|
m_upgradeNBTData = new HashMap<TurtleSide, NBTTagCompound>();
|
|
|
|
m_selectedSlot = 0;
|
|
m_fuelLevel = 0;
|
|
m_colour = null;
|
|
m_overlay = null;
|
|
|
|
m_instanceID = -1;
|
|
m_direction = EnumFacing.NORTH;
|
|
m_animation = TurtleAnimation.None;
|
|
m_animationProgress = 0;
|
|
m_lastAnimationProgress = 0;
|
|
}
|
|
|
|
public TurtleBrain getFutureSelf()
|
|
{
|
|
if( getOwner().getWorld().isRemote )
|
|
{
|
|
TurtleBrain futureSelf = getClientBrain( m_instanceID );
|
|
if( futureSelf != null )
|
|
{
|
|
return futureSelf;
|
|
}
|
|
}
|
|
return this;
|
|
}
|
|
|
|
public void setOwner( TileTurtle owner )
|
|
{
|
|
m_owner = owner;
|
|
}
|
|
|
|
public TileTurtle getOwner()
|
|
{
|
|
return m_owner;
|
|
}
|
|
|
|
public ComputerFamily getFamily()
|
|
{
|
|
return m_owner.getFamily();
|
|
}
|
|
|
|
public void setupComputer( ServerComputer computer )
|
|
{
|
|
updatePeripherals( computer );
|
|
}
|
|
|
|
public void update()
|
|
{
|
|
World world = getWorld();
|
|
if( !world.isRemote )
|
|
{
|
|
// Advance movement
|
|
updateCommands();
|
|
}
|
|
|
|
// Advance animation
|
|
updateAnimation();
|
|
|
|
// Advance upgrades
|
|
if( !m_upgrades.isEmpty() )
|
|
{
|
|
for( Map.Entry<TurtleSide, ITurtleUpgrade> entry : m_upgrades.entrySet() )
|
|
{
|
|
entry.getValue().update( this, entry.getKey() );
|
|
}
|
|
}
|
|
}
|
|
|
|
public void readFromNBT( NBTTagCompound nbttagcompound )
|
|
{
|
|
// Read state
|
|
m_direction = EnumFacing.getFront( nbttagcompound.getInteger( "dir" ) );
|
|
m_selectedSlot = nbttagcompound.getInteger( "selectedSlot" );
|
|
if( nbttagcompound.hasKey( "fuelLevel" ) )
|
|
{
|
|
m_fuelLevel = nbttagcompound.getInteger( "fuelLevel" );
|
|
}
|
|
else
|
|
{
|
|
m_fuelLevel = 0;
|
|
}
|
|
|
|
// Read colour
|
|
if( nbttagcompound.hasKey( "colourIndex" ) )
|
|
{
|
|
m_colour = Colour.values()[ nbttagcompound.getInteger( "colourIndex" ) ];
|
|
}
|
|
else
|
|
{
|
|
m_colour = null;
|
|
}
|
|
|
|
// Read overlay
|
|
if( nbttagcompound.hasKey( "overlay_mod" ) )
|
|
{
|
|
String overlay_mod = nbttagcompound.getString( "overlay_mod" );
|
|
if( nbttagcompound.hasKey( "overlay_path" ) )
|
|
{
|
|
String overlay_path = nbttagcompound.getString( "overlay_path" );
|
|
m_overlay = new ResourceLocation( overlay_mod, overlay_path );
|
|
}
|
|
else
|
|
{
|
|
m_overlay = null;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
m_overlay = null;
|
|
}
|
|
|
|
// Read upgrades
|
|
// (pre-1.4 turtles will have a "subType" variable, newer things will have "leftUpgrade" and "rightUpgrade")
|
|
ITurtleUpgrade leftUpgrade = null;
|
|
ITurtleUpgrade rightUpgrade = null;
|
|
if( nbttagcompound.hasKey( "subType" ) )
|
|
{
|
|
// Loading a pre-1.4 world
|
|
int subType = nbttagcompound.getInteger( "subType" );
|
|
if( (subType & 0x1) > 0 )
|
|
{
|
|
leftUpgrade = ComputerCraft.Upgrades.diamondPickaxe;
|
|
}
|
|
if( (subType & 0x2) > 0 )
|
|
{
|
|
rightUpgrade = ComputerCraft.Upgrades.wirelessModem;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
// Loading a post-1.4 world
|
|
if( nbttagcompound.hasKey( "leftUpgrade" ) )
|
|
{
|
|
if( nbttagcompound.getTagId( "leftUpgrade" ) == Constants.NBT.TAG_STRING )
|
|
{
|
|
leftUpgrade = ComputerCraft.getTurtleUpgrade( nbttagcompound.getString( "leftUpgrade" ) );
|
|
}
|
|
else
|
|
{
|
|
leftUpgrade = ComputerCraft.getTurtleUpgrade( nbttagcompound.getShort( "leftUpgrade" ) );
|
|
}
|
|
}
|
|
if( nbttagcompound.hasKey( "rightUpgrade" ) )
|
|
{
|
|
if( nbttagcompound.getTagId( "rightUpgrade" ) == Constants.NBT.TAG_STRING )
|
|
{
|
|
rightUpgrade = ComputerCraft.getTurtleUpgrade( nbttagcompound.getString( "rightUpgrade" ) );
|
|
}
|
|
else
|
|
{
|
|
rightUpgrade = ComputerCraft.getTurtleUpgrade( nbttagcompound.getShort( "rightUpgrade" ) );
|
|
}
|
|
}
|
|
}
|
|
setUpgrade( TurtleSide.Left, leftUpgrade );
|
|
setUpgrade( TurtleSide.Right, rightUpgrade );
|
|
|
|
// NBT
|
|
m_upgradeNBTData.clear();
|
|
if( nbttagcompound.hasKey( "leftUpgradeNBT" ) )
|
|
{
|
|
m_upgradeNBTData.put( TurtleSide.Left, (NBTTagCompound) nbttagcompound.getCompoundTag( "leftUpgradeNBT" ).copy() );
|
|
}
|
|
if( nbttagcompound.hasKey( "rightUpgradeNBT" ) )
|
|
{
|
|
m_upgradeNBTData.put( TurtleSide.Right, (NBTTagCompound) nbttagcompound.getCompoundTag( "rightUpgradeNBT" ).copy() );
|
|
}
|
|
}
|
|
|
|
public NBTTagCompound writeToNBT( NBTTagCompound nbttagcompound )
|
|
{
|
|
// Write state
|
|
nbttagcompound.setInteger( "dir", m_direction.getIndex() );
|
|
nbttagcompound.setInteger( "selectedSlot", m_selectedSlot );
|
|
nbttagcompound.setInteger( "fuelLevel", m_fuelLevel );
|
|
|
|
// Write upgrades
|
|
String leftUpgradeID = getUpgradeID( getUpgrade( TurtleSide.Left ) );
|
|
if( leftUpgradeID != null )
|
|
{
|
|
nbttagcompound.setString( "leftUpgrade", leftUpgradeID );
|
|
}
|
|
String rightUpgradeID = getUpgradeID( getUpgrade( TurtleSide.Right ) );
|
|
if( rightUpgradeID != null )
|
|
{
|
|
nbttagcompound.setString( "rightUpgrade", rightUpgradeID );
|
|
}
|
|
|
|
// Write colour
|
|
if( m_colour != null )
|
|
{
|
|
nbttagcompound.setInteger( "colourIndex", m_colour.ordinal() );
|
|
}
|
|
|
|
// Write overlay
|
|
if( m_overlay != null )
|
|
{
|
|
nbttagcompound.setString( "overlay_mod", m_overlay.getResourceDomain() );
|
|
nbttagcompound.setString( "overlay_path", m_overlay.getResourcePath() );
|
|
}
|
|
|
|
// Write NBT
|
|
if( m_upgradeNBTData.containsKey( TurtleSide.Left ) )
|
|
{
|
|
nbttagcompound.setTag( "leftUpgradeNBT", (NBTTagCompound) getUpgradeNBTData( TurtleSide.Left ).copy() );
|
|
}
|
|
if( m_upgradeNBTData.containsKey( TurtleSide.Right ) )
|
|
{
|
|
nbttagcompound.setTag( "rightUpgradeNBT", (NBTTagCompound) getUpgradeNBTData( TurtleSide.Right ).copy() );
|
|
}
|
|
|
|
return nbttagcompound;
|
|
}
|
|
|
|
private String getUpgradeID( ITurtleUpgrade upgrade )
|
|
{
|
|
if( upgrade != null )
|
|
{
|
|
return upgrade.getUpgradeID().toString();
|
|
}
|
|
return null;
|
|
}
|
|
|
|
public void writeDescription( NBTTagCompound nbttagcompound )
|
|
{
|
|
// Upgrades
|
|
String leftUpgradeID = getUpgradeID( getUpgrade( TurtleSide.Left ) );
|
|
if( leftUpgradeID != null )
|
|
{
|
|
nbttagcompound.setString( "leftUpgrade", leftUpgradeID );
|
|
}
|
|
String rightUpgradeID = getUpgradeID( getUpgrade( TurtleSide.Right ) );
|
|
if( rightUpgradeID != null )
|
|
{
|
|
nbttagcompound.setString( "rightUpgrade", rightUpgradeID );
|
|
}
|
|
|
|
// NBT
|
|
if( m_upgradeNBTData.containsKey( TurtleSide.Left ) )
|
|
{
|
|
nbttagcompound.setTag( "leftUpgradeNBT", (NBTTagCompound) getUpgradeNBTData( TurtleSide.Left ).copy() );
|
|
}
|
|
if( m_upgradeNBTData.containsKey( TurtleSide.Right ) )
|
|
{
|
|
nbttagcompound.setTag( "rightUpgradeNBT", (NBTTagCompound) getUpgradeNBTData( TurtleSide.Right ).copy() );
|
|
}
|
|
|
|
// Colour
|
|
if( m_colour != null )
|
|
{
|
|
nbttagcompound.setInteger( "colourIndex", m_colour.ordinal() );
|
|
}
|
|
|
|
// Overlay
|
|
if( m_overlay != null )
|
|
{
|
|
nbttagcompound.setString( "overlay_mod", m_overlay.getResourceDomain() );
|
|
nbttagcompound.setString( "overlay_path", m_overlay.getResourcePath() );
|
|
}
|
|
|
|
// Animation
|
|
if( m_instanceID < 0 )
|
|
{
|
|
m_instanceID = assignInstanceID();
|
|
}
|
|
nbttagcompound.setInteger( "brainInstanceID", m_instanceID );
|
|
nbttagcompound.setInteger( "animation", m_animation.ordinal() );
|
|
nbttagcompound.setInteger( "direction", m_direction.getIndex() );
|
|
nbttagcompound.setInteger( "fuelLevel", m_fuelLevel );
|
|
}
|
|
|
|
public void readDescription( NBTTagCompound nbttagcompound )
|
|
{
|
|
// Upgrades
|
|
if( nbttagcompound.hasKey( "leftUpgrade" ) )
|
|
{
|
|
setUpgrade( TurtleSide.Left, ComputerCraft.getTurtleUpgrade( nbttagcompound.getString( "leftUpgrade" ) ) );
|
|
}
|
|
else
|
|
{
|
|
setUpgrade( TurtleSide.Left, null );
|
|
}
|
|
if( nbttagcompound.hasKey( "rightUpgrade" ) )
|
|
{
|
|
setUpgrade( TurtleSide.Right, ComputerCraft.getTurtleUpgrade( nbttagcompound.getString( "rightUpgrade" ) ) );
|
|
}
|
|
else
|
|
{
|
|
setUpgrade( TurtleSide.Right, null );
|
|
}
|
|
|
|
// NBT
|
|
m_upgradeNBTData.clear();
|
|
if( nbttagcompound.hasKey( "leftUpgradeNBT" ) )
|
|
{
|
|
m_upgradeNBTData.put( TurtleSide.Left, (NBTTagCompound) nbttagcompound.getCompoundTag( "leftUpgradeNBT" ).copy() );
|
|
}
|
|
if( nbttagcompound.hasKey( "rightUpgradeNBT" ) )
|
|
{
|
|
m_upgradeNBTData.put( TurtleSide.Right, (NBTTagCompound)nbttagcompound.getCompoundTag( "rightUpgradeNBT" ).copy() );
|
|
}
|
|
|
|
// Colour
|
|
if( nbttagcompound.hasKey( "colourIndex" ) )
|
|
{
|
|
m_colour = Colour.values()[ nbttagcompound.getInteger( "colourIndex" ) ];
|
|
}
|
|
else
|
|
{
|
|
m_colour = null;
|
|
}
|
|
|
|
// Overlay
|
|
if( nbttagcompound.hasKey( "overlay_mod" ) && nbttagcompound.hasKey( "overlay_path" ) )
|
|
{
|
|
String overlay_mod = nbttagcompound.getString( "overlay_mod" );
|
|
String overlay_path = nbttagcompound.getString( "overlay_path" );
|
|
m_overlay = new ResourceLocation( overlay_mod, overlay_path );
|
|
}
|
|
else
|
|
{
|
|
m_overlay = null;
|
|
}
|
|
|
|
// Animation
|
|
m_instanceID = nbttagcompound.getInteger( "brainInstanceID" );
|
|
setClientBrain( m_instanceID, this );
|
|
|
|
TurtleAnimation anim = TurtleAnimation.values()[ nbttagcompound.getInteger( "animation" ) ];
|
|
if( anim != m_animation &&
|
|
anim != TurtleAnimation.Wait &&
|
|
anim != TurtleAnimation.ShortWait &&
|
|
anim != TurtleAnimation.None )
|
|
{
|
|
m_animation = TurtleAnimation.values()[ nbttagcompound.getInteger( "animation" ) ];
|
|
m_animationProgress = 0;
|
|
m_lastAnimationProgress = 0;
|
|
}
|
|
|
|
m_direction = EnumFacing.getFront( nbttagcompound.getInteger( "direction" ) );
|
|
m_fuelLevel = nbttagcompound.getInteger( "fuelLevel" );
|
|
}
|
|
|
|
@Override
|
|
public World getWorld()
|
|
{
|
|
return m_owner.getWorld();
|
|
}
|
|
|
|
@Override
|
|
public BlockPos getPosition()
|
|
{
|
|
return m_owner.getPos();
|
|
}
|
|
|
|
@Override
|
|
public boolean teleportTo( World world, BlockPos pos )
|
|
{
|
|
if( world.isRemote || getWorld().isRemote )
|
|
{
|
|
throw new UnsupportedOperationException();
|
|
}
|
|
|
|
// Cache info about the old turtle (so we don't access this after we delete ourselves)
|
|
World oldWorld = getWorld();
|
|
BlockPos oldPos = m_owner.getPos();
|
|
Block oldBlock = m_owner.getBlock();
|
|
|
|
if( oldWorld == world && oldPos.equals( pos ) )
|
|
{
|
|
// Teleporting to the current position is a no-op
|
|
return true;
|
|
}
|
|
|
|
// Create a new turtle
|
|
if( world.isBlockLoaded( pos ) && world.setBlockState( pos, oldBlock.getDefaultState(), 0 ) )
|
|
{
|
|
Block block = world.getBlockState( pos ).getBlock();
|
|
if( block == oldBlock )
|
|
{
|
|
TileEntity newTile = world.getTileEntity( pos );
|
|
if( newTile != null && newTile instanceof TileTurtle )
|
|
{
|
|
// Copy the old turtle state into the new turtle
|
|
TileTurtle newTurtle = (TileTurtle)newTile;
|
|
newTurtle.setWorldObj( world );
|
|
newTurtle.setPos( pos );
|
|
newTurtle.transferStateFrom( m_owner );
|
|
newTurtle.createServerComputer().setWorld( world );
|
|
newTurtle.createServerComputer().setPosition( pos );
|
|
|
|
// Remove the old turtle
|
|
oldWorld.setBlockToAir( oldPos );
|
|
|
|
// Make sure everybody knows about it
|
|
newTurtle.updateBlock();
|
|
newTurtle.updateInput();
|
|
newTurtle.updateOutput();
|
|
return true;
|
|
}
|
|
}
|
|
|
|
// Something went wrong, remove the newly created turtle
|
|
world.setBlockToAir( pos );
|
|
}
|
|
|
|
return false;
|
|
}
|
|
|
|
@Override
|
|
public Vec3d getVisualPosition( float f )
|
|
{
|
|
Vec3d offset = getRenderOffset( f );
|
|
BlockPos pos = m_owner.getPos();
|
|
return new Vec3d(
|
|
pos.getX() + 0.5 + offset.xCoord,
|
|
pos.getY() + 0.5 + offset.yCoord,
|
|
pos.getZ() + 0.5 + offset.zCoord
|
|
);
|
|
}
|
|
|
|
@Override
|
|
public float getVisualYaw( float f )
|
|
{
|
|
float forward = DirectionUtil.toYawAngle( getDirection() );
|
|
float yaw = forward;
|
|
switch( m_animation )
|
|
{
|
|
case TurnLeft:
|
|
{
|
|
yaw += 90.0f * (1.0f - getAnimationFraction( f ));
|
|
if( yaw >= 360.0f )
|
|
{
|
|
yaw -= 360.0f;
|
|
}
|
|
break;
|
|
}
|
|
case TurnRight:
|
|
{
|
|
yaw += -90.0f * (1.0f - getAnimationFraction( f ));
|
|
if( yaw < 0.0f )
|
|
{
|
|
yaw += 360.0f;
|
|
}
|
|
break;
|
|
}
|
|
}
|
|
return yaw;
|
|
}
|
|
|
|
@Override
|
|
public EnumFacing getDirection()
|
|
{
|
|
return m_direction;
|
|
}
|
|
|
|
@Override
|
|
public void setDirection( EnumFacing dir )
|
|
{
|
|
if( dir.getAxis() == EnumFacing.Axis.Y )
|
|
{
|
|
dir = EnumFacing.NORTH;
|
|
}
|
|
m_direction = dir;
|
|
m_owner.updateOutput();
|
|
m_owner.updateInput();
|
|
m_owner.onTileEntityChange();
|
|
}
|
|
|
|
@Override
|
|
public int getSelectedSlot()
|
|
{
|
|
return m_selectedSlot;
|
|
}
|
|
|
|
@Override
|
|
public void setSelectedSlot( int slot )
|
|
{
|
|
if( getWorld().isRemote )
|
|
{
|
|
throw new UnsupportedOperationException();
|
|
}
|
|
if( slot >= 0 && slot < m_owner.getSizeInventory() )
|
|
{
|
|
m_selectedSlot = slot;
|
|
m_owner.onTileEntityChange();
|
|
}
|
|
}
|
|
|
|
@Override
|
|
public IInventory getInventory()
|
|
{
|
|
return m_owner;
|
|
}
|
|
|
|
@Override
|
|
public boolean isFuelNeeded()
|
|
{
|
|
return ComputerCraft.turtlesNeedFuel;
|
|
}
|
|
|
|
@Override
|
|
public int getFuelLevel()
|
|
{
|
|
return Math.min( m_fuelLevel, getFuelLimit() );
|
|
}
|
|
|
|
@Override
|
|
public void setFuelLevel( int level )
|
|
{
|
|
m_fuelLevel = Math.min( level, getFuelLimit() );
|
|
m_owner.onTileEntityChange();
|
|
}
|
|
|
|
@Override
|
|
public int getFuelLimit()
|
|
{
|
|
if( m_owner.getFamily() == ComputerFamily.Advanced )
|
|
{
|
|
return ComputerCraft.advancedTurtleFuelLimit;
|
|
}
|
|
else
|
|
{
|
|
return ComputerCraft.turtleFuelLimit;
|
|
}
|
|
}
|
|
|
|
@Override
|
|
public boolean consumeFuel( int fuel )
|
|
{
|
|
if( getWorld().isRemote )
|
|
{
|
|
throw new UnsupportedOperationException();
|
|
}
|
|
if( !isFuelNeeded() )
|
|
{
|
|
return true;
|
|
}
|
|
|
|
int consumption = Math.max( fuel, 0 );
|
|
if( getFuelLevel() >= consumption )
|
|
{
|
|
setFuelLevel( getFuelLevel() - consumption );
|
|
return true;
|
|
}
|
|
return false;
|
|
}
|
|
|
|
@Override
|
|
public void addFuel( int fuel )
|
|
{
|
|
if( getWorld().isRemote )
|
|
{
|
|
throw new UnsupportedOperationException();
|
|
}
|
|
int addition = Math.max( fuel, 0 );
|
|
setFuelLevel( getFuelLevel() + addition );
|
|
}
|
|
|
|
private int issueCommand( ITurtleCommand command )
|
|
{
|
|
m_commandQueue.offer( new TurtleCommandQueueEntry( ++m_commandsIssued, command ) );
|
|
return m_commandsIssued;
|
|
}
|
|
|
|
@Override
|
|
public Object[] executeCommand( ILuaContext context, ITurtleCommand command ) throws LuaException, InterruptedException
|
|
{
|
|
if( getWorld().isRemote )
|
|
{
|
|
throw new UnsupportedOperationException();
|
|
}
|
|
|
|
// Issue command
|
|
int commandID = issueCommand( command );
|
|
|
|
// Wait for response
|
|
while( true )
|
|
{
|
|
Object[] response = context.pullEvent( "turtle_response" );
|
|
if( response.length >= 3 && response[ 1 ] instanceof Number && response[ 2 ] instanceof Boolean )
|
|
{
|
|
if( ( (Number) response[ 1 ] ).intValue() == commandID )
|
|
{
|
|
Object[] returnValues = new Object[ response.length - 2 ];
|
|
for( int i = 0; i < returnValues.length; ++i )
|
|
{
|
|
returnValues[ i ] = response[ i + 2 ];
|
|
}
|
|
return returnValues;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
@Override
|
|
public void playAnimation( TurtleAnimation animation )
|
|
{
|
|
if( getWorld().isRemote )
|
|
{
|
|
throw new UnsupportedOperationException();
|
|
}
|
|
m_animation = animation;
|
|
if( m_animation == TurtleAnimation.ShortWait )
|
|
{
|
|
m_animationProgress = ANIM_DURATION / 2;
|
|
m_lastAnimationProgress = ANIM_DURATION / 2;
|
|
}
|
|
else
|
|
{
|
|
m_animationProgress = 0;
|
|
m_lastAnimationProgress = 0;
|
|
}
|
|
m_owner.updateBlock();
|
|
}
|
|
|
|
@Override
|
|
public int getDyeColour()
|
|
{
|
|
return (m_colour != null) ? m_colour.ordinal() : -1;
|
|
}
|
|
|
|
public ResourceLocation getOverlay()
|
|
{
|
|
return m_overlay;
|
|
}
|
|
|
|
public void setOverlay( ResourceLocation overlay )
|
|
{
|
|
if( !Objects.equal(m_overlay, overlay) )
|
|
{
|
|
m_overlay = overlay;
|
|
m_owner.updateBlock();
|
|
}
|
|
}
|
|
|
|
@Override
|
|
public void setDyeColour( int dyeColour )
|
|
{
|
|
Colour newColour = null;
|
|
if( dyeColour >= 0 && dyeColour < 16 )
|
|
{
|
|
newColour = Colour.values()[ dyeColour ];
|
|
}
|
|
if( m_colour != newColour )
|
|
{
|
|
m_colour = newColour;
|
|
m_owner.updateBlock();
|
|
}
|
|
}
|
|
|
|
@Override
|
|
public ITurtleUpgrade getUpgrade( TurtleSide side )
|
|
{
|
|
if( m_upgrades.containsKey( side ) )
|
|
{
|
|
return m_upgrades.get( side );
|
|
}
|
|
return null;
|
|
}
|
|
|
|
@Override
|
|
public void setUpgrade( TurtleSide side, ITurtleUpgrade upgrade )
|
|
{
|
|
// Remove old upgrade
|
|
if( m_upgrades.containsKey( side ) )
|
|
{
|
|
if( m_upgrades.get( side ) == upgrade )
|
|
{
|
|
return;
|
|
}
|
|
m_upgrades.remove( side );
|
|
}
|
|
else
|
|
{
|
|
if( upgrade == null )
|
|
{
|
|
return;
|
|
}
|
|
}
|
|
if( m_upgradeNBTData.containsKey( side ) )
|
|
{
|
|
m_upgradeNBTData.remove( side );
|
|
}
|
|
|
|
// Set new upgrade
|
|
if( upgrade != null )
|
|
{
|
|
m_upgrades.put( side, upgrade );
|
|
}
|
|
|
|
// Notify clients and create peripherals
|
|
if( m_owner.getWorld() != null )
|
|
{
|
|
updatePeripherals( m_owner.createServerComputer() );
|
|
m_owner.updateBlock();
|
|
}
|
|
}
|
|
|
|
@Override
|
|
public IPeripheral getPeripheral( TurtleSide side )
|
|
{
|
|
if( m_peripherals.containsKey( side ) )
|
|
{
|
|
return m_peripherals.get( side );
|
|
}
|
|
return null;
|
|
}
|
|
|
|
@Override
|
|
public NBTTagCompound getUpgradeNBTData( TurtleSide side )
|
|
{
|
|
if( !m_upgradeNBTData.containsKey( side ) )
|
|
{
|
|
m_upgradeNBTData.put( side, new NBTTagCompound() );
|
|
}
|
|
return m_upgradeNBTData.get( side );
|
|
}
|
|
|
|
@Override
|
|
public void updateUpgradeNBTData( TurtleSide side )
|
|
{
|
|
m_owner.updateBlock();
|
|
}
|
|
|
|
public boolean saveBlockChange( BlockPos coordinates, IBlockState previousState )
|
|
{
|
|
// Overriden by CCEdu
|
|
return false;
|
|
}
|
|
|
|
public Vec3d getRenderOffset( float f )
|
|
{
|
|
switch( m_animation )
|
|
{
|
|
case MoveForward:
|
|
case MoveBack:
|
|
case MoveUp:
|
|
case MoveDown:
|
|
{
|
|
// Get direction
|
|
EnumFacing dir;
|
|
switch( m_animation )
|
|
{
|
|
case MoveForward:
|
|
default:
|
|
{
|
|
dir = getDirection();
|
|
break;
|
|
}
|
|
case MoveBack:
|
|
{
|
|
dir = getDirection().getOpposite();
|
|
break;
|
|
}
|
|
case MoveUp:
|
|
{
|
|
dir = EnumFacing.UP;
|
|
break;
|
|
}
|
|
case MoveDown:
|
|
{
|
|
dir = EnumFacing.DOWN;
|
|
break;
|
|
}
|
|
}
|
|
|
|
double distance = -1.0 + (double)getAnimationFraction( f );
|
|
return new Vec3d(
|
|
distance * (double)dir.getFrontOffsetX(),
|
|
distance * (double)dir.getFrontOffsetY(),
|
|
distance * (double)dir.getFrontOffsetZ()
|
|
);
|
|
}
|
|
default:
|
|
{
|
|
return new Vec3d( 0.0, 0.0, 0.0 );
|
|
}
|
|
}
|
|
}
|
|
|
|
public float getToolRenderAngle( TurtleSide side, float f )
|
|
{
|
|
if( (side == TurtleSide.Left && m_animation == TurtleAnimation.SwingLeftTool) ||
|
|
(side == TurtleSide.Right && m_animation == TurtleAnimation.SwingRightTool) )
|
|
{
|
|
return 45.0f * (float)Math.sin( (double) getAnimationFraction( f ) * Math.PI );
|
|
}
|
|
return 0.0f;
|
|
}
|
|
|
|
private int toDirection( TurtleSide side )
|
|
{
|
|
switch( side )
|
|
{
|
|
case Left:
|
|
{
|
|
return 5;
|
|
}
|
|
case Right:
|
|
default:
|
|
{
|
|
return 4;
|
|
}
|
|
}
|
|
}
|
|
|
|
public void updatePeripherals( ServerComputer serverComputer )
|
|
{
|
|
if( serverComputer == null )
|
|
{
|
|
// Nothing to do
|
|
return;
|
|
}
|
|
|
|
// Update peripherals
|
|
for( TurtleSide side : TurtleSide.values() )
|
|
{
|
|
ITurtleUpgrade upgrade = getUpgrade( side );
|
|
IPeripheral peripheral = null;
|
|
if( upgrade != null && upgrade.getType() == TurtleUpgradeType.Peripheral )
|
|
{
|
|
peripheral = upgrade.createPeripheral( this, side );
|
|
}
|
|
|
|
int dir = toDirection( side );
|
|
if( peripheral != null )
|
|
{
|
|
if( !m_peripherals.containsKey( side ) )
|
|
{
|
|
serverComputer.setPeripheral( dir, peripheral );
|
|
serverComputer.setRedstoneInput( dir, 0 );
|
|
serverComputer.setBundledRedstoneInput( dir, 0 );
|
|
m_peripherals.put( side, peripheral );
|
|
}
|
|
else if( !m_peripherals.get( side ).equals( peripheral ) )
|
|
{
|
|
serverComputer.setPeripheral( dir, peripheral );
|
|
serverComputer.setRedstoneInput( dir, 0 );
|
|
serverComputer.setBundledRedstoneInput( dir, 0 );
|
|
m_peripherals.remove( side );
|
|
m_peripherals.put( side, peripheral );
|
|
}
|
|
}
|
|
else if( m_peripherals.containsKey( side ) )
|
|
{
|
|
serverComputer.setPeripheral( dir, null );
|
|
m_peripherals.remove( side );
|
|
}
|
|
}
|
|
}
|
|
|
|
private void updateCommands()
|
|
{
|
|
if( m_animation == TurtleAnimation.None )
|
|
{
|
|
// Pull a new command
|
|
TurtleCommandQueueEntry nextCommand = null;
|
|
if( m_commandQueue.peek() != null )
|
|
{
|
|
nextCommand = m_commandQueue.remove();
|
|
}
|
|
|
|
if( nextCommand != null )
|
|
{
|
|
// Execute the command
|
|
TurtleCommandResult result = nextCommand.command.execute( this );
|
|
|
|
// Dispatch the callback
|
|
int callbackID = nextCommand.callbackID;
|
|
if( callbackID >= 0 )
|
|
{
|
|
if( result != null && result.isSuccess() )
|
|
{
|
|
IComputer computer = m_owner.getComputer();
|
|
if( computer != null )
|
|
{
|
|
Object[] results = result.getResults();
|
|
if( results != null )
|
|
{
|
|
Object[] arguments = new Object[ results.length + 2 ];
|
|
arguments[0] = callbackID;
|
|
arguments[1] = true;
|
|
for( int i=0; i<results.length; ++i )
|
|
{
|
|
arguments[2+i] = results[i];
|
|
}
|
|
computer.queueEvent( "turtle_response", arguments );
|
|
}
|
|
else
|
|
{
|
|
computer.queueEvent( "turtle_response", new Object[] {
|
|
callbackID, true
|
|
} );
|
|
}
|
|
}
|
|
}
|
|
else
|
|
{
|
|
IComputer computer = m_owner.getComputer();
|
|
if( computer != null )
|
|
{
|
|
computer.queueEvent( "turtle_response", new Object[] {
|
|
callbackID, false, ( result != null ) ? result.getErrorMessage() : null
|
|
} );
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
private void updateAnimation()
|
|
{
|
|
if( m_animation != TurtleAnimation.None )
|
|
{
|
|
World world = this.getWorld();
|
|
|
|
if( ComputerCraft.turtlesCanPush )
|
|
{
|
|
// Advance entity pushing
|
|
if( m_animation == TurtleAnimation.MoveForward ||
|
|
m_animation == TurtleAnimation.MoveBack ||
|
|
m_animation == TurtleAnimation.MoveUp ||
|
|
m_animation == TurtleAnimation.MoveDown )
|
|
{
|
|
BlockPos pos = getPosition();
|
|
EnumFacing moveDir;
|
|
switch( m_animation )
|
|
{
|
|
case MoveForward:
|
|
default:
|
|
{
|
|
moveDir = m_direction;
|
|
break;
|
|
}
|
|
case MoveBack:
|
|
{
|
|
moveDir = m_direction.getOpposite();
|
|
break;
|
|
}
|
|
case MoveUp:
|
|
{
|
|
moveDir = EnumFacing.UP;
|
|
break;
|
|
}
|
|
case MoveDown:
|
|
{
|
|
moveDir = EnumFacing.DOWN;
|
|
break;
|
|
}
|
|
}
|
|
|
|
double minX = pos.getX();
|
|
double minY = pos.getY();
|
|
double minZ = pos.getZ();
|
|
double maxX = minX + 1.0;
|
|
double maxY = minY + 1.0;
|
|
double maxZ = minZ + 1.0;
|
|
|
|
float pushFrac = 1.0f - ((float)(m_animationProgress + 1) / (float)ANIM_DURATION);
|
|
float push = Math.max( pushFrac + 0.0125f, 0.0f );
|
|
if (moveDir.getFrontOffsetX() < 0)
|
|
{
|
|
minX += (double)((float)moveDir.getFrontOffsetX() * push);
|
|
}
|
|
else
|
|
{
|
|
maxX -= (double)((float)moveDir.getFrontOffsetX() * push);
|
|
}
|
|
|
|
if (moveDir.getFrontOffsetY() < 0)
|
|
{
|
|
minY += (double)((float)moveDir.getFrontOffsetY() * push);
|
|
}
|
|
else
|
|
{
|
|
maxY -= (double)((float)moveDir.getFrontOffsetY() * push);
|
|
}
|
|
|
|
if (moveDir.getFrontOffsetZ() < 0)
|
|
{
|
|
minZ += (double)((float)moveDir.getFrontOffsetZ() * push);
|
|
}
|
|
else
|
|
{
|
|
maxZ -= (double)((float)moveDir.getFrontOffsetZ() * push);
|
|
}
|
|
|
|
AxisAlignedBB aabb = new AxisAlignedBB( minX, minY, minZ, maxX, maxY, maxZ );
|
|
List list = world.getEntitiesWithinAABBExcludingEntity( (Entity)null, aabb );
|
|
if( !list.isEmpty() )
|
|
{
|
|
double pushStep = 1.0f / (float) ANIM_DURATION;
|
|
double pushStepX = (double) moveDir.getFrontOffsetX() * pushStep;
|
|
double pushStepY = (double) moveDir.getFrontOffsetY() * pushStep;
|
|
double pushStepZ = (double) moveDir.getFrontOffsetZ() * pushStep;
|
|
for( int i = 0; i < list.size(); ++i )
|
|
{
|
|
Entity entity = (Entity) list.get( i );
|
|
entity.moveEntity(
|
|
pushStepX, pushStepY, pushStepZ
|
|
);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
// Advance valentines day easter egg
|
|
if( world.isRemote && m_animation == TurtleAnimation.MoveForward && m_animationProgress == 4 )
|
|
{
|
|
// Spawn love pfx if valentines day
|
|
Holiday currentHoliday = HolidayUtil.getCurrentHoliday();
|
|
if( currentHoliday == Holiday.Valentines )
|
|
{
|
|
Vec3d position = getVisualPosition( 1.0f );
|
|
if( position != null )
|
|
{
|
|
double x = position.xCoord + world.rand.nextGaussian() * 0.1;
|
|
double y = position.yCoord + 0.5 + world.rand.nextGaussian() * 0.1;
|
|
double z = position.zCoord + world.rand.nextGaussian() * 0.1;
|
|
world.spawnParticle(
|
|
EnumParticleTypes.HEART, x, y, z,
|
|
world.rand.nextGaussian() * 0.02,
|
|
world.rand.nextGaussian() * 0.02,
|
|
world.rand.nextGaussian() * 0.02
|
|
);
|
|
}
|
|
}
|
|
}
|
|
|
|
// Wait for anim completion
|
|
m_lastAnimationProgress = m_animationProgress;
|
|
if( ++m_animationProgress >= ANIM_DURATION )
|
|
{
|
|
m_animation = TurtleAnimation.None;
|
|
m_animationProgress = 0;
|
|
m_lastAnimationProgress = 0;
|
|
}
|
|
}
|
|
}
|
|
|
|
private float getAnimationFraction( float f )
|
|
{
|
|
float next = (float)m_animationProgress / ANIM_DURATION;
|
|
float previous = (float)m_lastAnimationProgress / ANIM_DURATION;
|
|
return previous + (next - previous) * f;
|
|
}
|
|
}
|