mirror of
https://github.com/SquidDev-CC/CC-Tweaked
synced 2024-07-04 11:03:22 +00:00
e85cdacbc5
Added the complete source code to ComputerCraft 1.79 for Minecraft 1.8.9, plus newly written README and LICENSE files for the open source release.
1177 lines
36 KiB
Java
1177 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.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 void 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() );
|
|
}
|
|
}
|
|
|
|
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(), 3 ) )
|
|
{
|
|
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.updateInput();
|
|
newTurtle.updateOutput();
|
|
return true;
|
|
}
|
|
}
|
|
|
|
// Something went wrong, remove the newly created turtle
|
|
world.setBlockToAir( pos );
|
|
}
|
|
|
|
return false;
|
|
}
|
|
|
|
@Override
|
|
public Vec3 getVisualPosition( float f )
|
|
{
|
|
Vec3 offset = getRenderOffset( f );
|
|
BlockPos pos = m_owner.getPos();
|
|
return new Vec3(
|
|
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 Vec3 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 Vec3(
|
|
distance * (double)dir.getFrontOffsetX(),
|
|
distance * (double)dir.getFrontOffsetY(),
|
|
distance * (double)dir.getFrontOffsetZ()
|
|
);
|
|
}
|
|
default:
|
|
{
|
|
return new Vec3( 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 )
|
|
{
|
|
Vec3 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;
|
|
}
|
|
}
|