diff --git a/src/main/java/dan200/computercraft/ComputerCraft.java b/src/main/java/dan200/computercraft/ComputerCraft.java index 710094ecd..d7f5edf80 100644 --- a/src/main/java/dan200/computercraft/ComputerCraft.java +++ b/src/main/java/dan200/computercraft/ComputerCraft.java @@ -14,6 +14,7 @@ import dan200.computercraft.api.media.IMediaProvider; import dan200.computercraft.api.peripheral.IPeripheral; import dan200.computercraft.api.peripheral.IPeripheralProvider; import dan200.computercraft.api.permissions.ITurtlePermissionProvider; +import dan200.computercraft.api.pocket.IPocketUpgrade; import dan200.computercraft.api.redstone.IBundledRedstoneProvider; import dan200.computercraft.api.turtle.ITurtleUpgrade; import dan200.computercraft.core.filesystem.ComboMount; @@ -38,15 +39,13 @@ import dan200.computercraft.shared.peripheral.modem.BlockAdvancedModem; import dan200.computercraft.shared.peripheral.modem.WirelessNetwork; import dan200.computercraft.shared.peripheral.printer.TilePrinter; import dan200.computercraft.shared.pocket.items.ItemPocketComputer; +import dan200.computercraft.shared.pocket.peripherals.PocketModem; import dan200.computercraft.shared.proxy.ICCTurtleProxy; import dan200.computercraft.shared.proxy.IComputerCraftProxy; import dan200.computercraft.shared.turtle.blocks.BlockTurtle; import dan200.computercraft.shared.turtle.blocks.TileTurtle; import dan200.computercraft.shared.turtle.upgrades.*; -import dan200.computercraft.shared.util.CreativeTabMain; -import dan200.computercraft.shared.util.IDAssigner; -import dan200.computercraft.shared.util.IEntityDropConsumer; -import dan200.computercraft.shared.util.WorldUtil; +import dan200.computercraft.shared.util.*; import io.netty.buffer.Unpooled; import net.minecraft.entity.Entity; import net.minecraft.entity.player.EntityPlayer; @@ -75,9 +74,7 @@ import java.io.IOException; import java.net.MalformedURLException; import java.net.URISyntaxException; import java.net.URL; -import java.util.ArrayList; -import java.util.Iterator; -import java.util.List; +import java.util.*; /////////////// // UNIVERSAL // @@ -163,6 +160,12 @@ public class ComputerCraft public static TurtleModem advancedModem; } + public static class PocketUpgrades + { + public static PocketModem wirelessModem; + public static PocketModem advancedModem; + } + public static class Config { public static Configuration config; @@ -186,7 +189,6 @@ public class ComputerCraft public static Property computerSpaceLimit; public static Property floppySpaceLimit; public static Property maximumFilesOpen; - } // Registries @@ -204,6 +206,7 @@ public class ComputerCraft private static List bundledRedstoneProviders = new ArrayList(); private static List mediaProviders = new ArrayList(); private static List permissionProviders = new ArrayList(); + private static final Map pocketUpgrades = new HashMap(); // Implementation @Mod.Instance( value = "ComputerCraft" ) @@ -539,6 +542,18 @@ public class ComputerCraft return true; } + public static void registerPocketUpgrade( IPocketUpgrade upgrade ) + { + String id = upgrade.getUpgradeID().toString(); + IPocketUpgrade existing = pocketUpgrades.get( id ); + if( existing != null ) + { + throw new RuntimeException( "Error registering '" + upgrade.getUnlocalisedAdjective() + " pocket computer'. UpgradeID '" + id + "' is already registered by '" + existing.getUnlocalisedAdjective() + " pocket computer'" ); + } + + pocketUpgrades.put( id, upgrade ); + } + public static void registerPeripheralProvider( IPeripheralProvider provider ) { if( provider != null && !peripheralProviders.contains( provider ) ) @@ -659,6 +674,37 @@ public class ComputerCraft return null; } + public static IPocketUpgrade getPocketUpgrade(String id) { + return pocketUpgrades.get( id ); + } + + public static IPocketUpgrade getPocketUpgrade( ItemStack stack ) + { + if( stack == null ) return null; + + for (IPocketUpgrade upgrade : pocketUpgrades.values()) + { + ItemStack craftingStack = upgrade.getCraftingItem(); + if( craftingStack != null && InventoryUtil.areItemsStackable( stack, craftingStack ) ) + { + return upgrade; + } + } + + return null; + } + + public static Iterable getVanillaPocketUpgrades() { + List upgrades = new ArrayList(); + for(IPocketUpgrade upgrade : pocketUpgrades.values()) { + if(upgrade instanceof PocketModem) { + upgrades.add( upgrade ); + } + } + + return upgrades; + } + public static int createUniqueNumberedSaveDir( World world, String parentSubPath ) { return IDAssigner.getNextIDFromDirectory(new File(getWorldDir(world), parentSubPath)); diff --git a/src/main/java/dan200/computercraft/api/ComputerCraftAPI.java b/src/main/java/dan200/computercraft/api/ComputerCraftAPI.java index c88bde40e..c4194f5b2 100644 --- a/src/main/java/dan200/computercraft/api/ComputerCraftAPI.java +++ b/src/main/java/dan200/computercraft/api/ComputerCraftAPI.java @@ -14,6 +14,7 @@ import dan200.computercraft.api.peripheral.IComputerAccess; import dan200.computercraft.api.peripheral.IPeripheral; import dan200.computercraft.api.peripheral.IPeripheralProvider; import dan200.computercraft.api.permissions.ITurtlePermissionProvider; +import dan200.computercraft.api.pocket.IPocketUpgrade; import dan200.computercraft.api.redstone.IBundledRedstoneProvider; import dan200.computercraft.api.turtle.ITurtleUpgrade; import net.minecraft.util.EnumFacing; @@ -270,6 +271,17 @@ public final class ComputerCraftAPI } } + public static void registerPocketUpgrade(IPocketUpgrade upgrade) { + findCC(); + if(computerCraft_registerPocketUpgrade != null) { + try { + computerCraft_registerPocketUpgrade.invoke( null, upgrade ); + } catch (Exception e) { + // It failed + } + } + } + // The functions below here are private, and are used to interface with the non-API ComputerCraft classes. // Reflection is used here so you can develop your mod without decompiling ComputerCraft and including // it in your solution, and so your mod won't crash if ComputerCraft is installed. @@ -308,6 +320,9 @@ public final class ComputerCraftAPI computerCraft_registerPermissionProvider = findCCMethod( "registerPermissionProvider", new Class[] { ITurtlePermissionProvider.class } ); + computerCraft_registerPocketUpgrade = findCCMethod( "registerPocketUpgrade", new Class[] { + IPocketUpgrade.class + } ); } catch( Exception e ) { System.out.println( "ComputerCraftAPI: ComputerCraft not found." ); } finally { @@ -342,4 +357,5 @@ public final class ComputerCraftAPI private static Method computerCraft_getDefaultBundledRedstoneOutput = null; private static Method computerCraft_registerMediaProvider = null; private static Method computerCraft_registerPermissionProvider = null; + private static Method computerCraft_registerPocketUpgrade = null; } diff --git a/src/main/java/dan200/computercraft/api/pocket/IPocketAccess.java b/src/main/java/dan200/computercraft/api/pocket/IPocketAccess.java new file mode 100644 index 000000000..2485cacf3 --- /dev/null +++ b/src/main/java/dan200/computercraft/api/pocket/IPocketAccess.java @@ -0,0 +1,77 @@ +package dan200.computercraft.api.pocket; + +import dan200.computercraft.api.peripheral.IPeripheral; +import net.minecraft.entity.Entity; +import net.minecraft.nbt.NBTTagCompound; +import net.minecraft.util.ResourceLocation; + +import javax.annotation.Nonnull; +import javax.annotation.Nullable; +import java.util.Map; + +/** + * Wrapper class for pocket computers + */ +public interface IPocketAccess +{ + /** + * Gets the entity holding this item. + * + * @return The holding entity. This may be {@code null}. + */ + @Nullable + Entity getEntity(); + + /** + * Get the colour of the pocket computer's light. + * + * See {@link #setLight(int)} for the values this may return. + * + * @return The colour of the pocket computer's light. + * @see #setLight(int) + */ + int getLight(); + + /** + * Set the colour of the pocket computer's light. Use {@link 0} to turn it off. + * + * Colours take the form of an integer between 0 and 15, using the opposite order to those in + * {@link The colors API} - so 0 being black, + * 1 representing red, 2 representing green all the way up to 15 for white. + * + * @param value The colour the light should have. + * @see #getLight() + */ + void setLight( int value ); + + /** + * Get the upgrade-specific NBT. + * + * This is persisted between computer reboots and chunk loads. + * + * @return The upgrade's NBT. + * @see #updateUpgradeNBTData() + */ + @Nonnull + NBTTagCompound getUpgradeNBTData(); + + /** + * Mark the upgrade-specific NBT as dirty. + * + * @see #getUpgradeNBTData() + */ + void updateUpgradeNBTData(); + + /** + * Remove the current peripheral and create a new one. You may wish to do this if the methods available change. + */ + void invalidatePeripheral(); + + /** + * Get a list of all upgrades for the pocket computer. + * + * @return A collection of all upgrade names. + */ + @Nonnull + Map getUpgrades(); +} diff --git a/src/main/java/dan200/computercraft/api/pocket/IPocketUpgrade.java b/src/main/java/dan200/computercraft/api/pocket/IPocketUpgrade.java new file mode 100644 index 000000000..4b92ac214 --- /dev/null +++ b/src/main/java/dan200/computercraft/api/pocket/IPocketUpgrade.java @@ -0,0 +1,91 @@ +package dan200.computercraft.api.pocket; + +import dan200.computercraft.api.ComputerCraftAPI; +import dan200.computercraft.api.peripheral.IPeripheral; +import dan200.computercraft.api.turtle.ITurtleUpgrade; +import net.minecraft.item.ItemStack; +import net.minecraft.util.ResourceLocation; +import net.minecraft.world.World; + +import javax.annotation.Nonnull; +import javax.annotation.Nullable; + +/** + * Additional peripherals for pocket computers. + * + * This is similar to {@link dan200.computercraft.api.turtle.ITurtleUpgrade}. + */ +public interface IPocketUpgrade +{ + + /** + * Gets a unique identifier representing this type of turtle upgrade. eg: "computercraft:wireless_modem" or + * "my_mod:my_upgrade". + * + * You should use a unique resource domain to ensure this upgrade is uniquely identified. The upgrade will fail + * registration if an already used ID is specified. + * + * @return The upgrade's id. + * @see IPocketUpgrade#getUpgradeID() + * @see ComputerCraftAPI#registerPocketUpgrade(IPocketUpgrade) + */ + @Nonnull + ResourceLocation getUpgradeID(); + + /** + * Return an unlocalised string to describe the type of pocket computer this upgrade provides. + * + * An example of a built-in adjectives is "Wireless" - this is converted to "Wireless Pocket Computer". + * + * @return The unlocalised adjective. + * @see ITurtleUpgrade#getUnlocalisedAdjective() + */ + @Nonnull + String getUnlocalisedAdjective(); + + /** + * Return an item stack representing the type of item that a pocket computer must be crafted with to create a + * pocket computer which holds this upgrade. This item stack is also used to determine the upgrade given by + * {@code pocket.equip()}/{@code pocket.unequip()}. + * + * @return The item stack used for crafting. This can be {@code null} if crafting is disabled. + */ + @Nullable + ItemStack getCraftingItem(); + + /** + * Creates a peripheral for the pocket computer. + * + * The peripheral created will be stored for the lifetime of the upgrade, will be passed an argument to + * {@link #update(IPocketAccess, IPeripheral)} and will be attached, detached and have methods called in the same + * manner as an ordinary peripheral. + * + * @param access The access object for the pocket item stack. + * @return The newly created peripheral. + * @see #update(IPocketAccess, IPeripheral) + */ + @Nullable + IPeripheral createPeripheral( @Nonnull IPocketAccess access ); + + /** + * Called when the pocket computer item stack updates. + * + * @param access The access object for the pocket item stack. + * @param peripheral The peripheral for this upgrade. + * @see #createPeripheral(IPocketAccess) + */ + void update( @Nonnull IPocketAccess access, @Nullable IPeripheral peripheral ); + + /** + * Called when the pocket computer is right clicked. + * + * @param world The world the computer is in. + * @param access The access object for the pocket item stack. + * @param peripheral The peripheral for this upgrade. + * @return {@code true} to stop the GUI from opening, otherwise false. You should always provide some code path + * which returns {@code false}, such as requiring the player to be sneaking - otherwise they will be unable to + * access the GUI. + * @see #createPeripheral(IPocketAccess) + */ + boolean onRightClick( @Nonnull World world, @Nonnull IPocketAccess access, @Nullable IPeripheral peripheral ); +} diff --git a/src/main/java/dan200/computercraft/client/proxy/ComputerCraftProxyClient.java b/src/main/java/dan200/computercraft/client/proxy/ComputerCraftProxyClient.java index eb3366d1e..7dab83860 100644 --- a/src/main/java/dan200/computercraft/client/proxy/ComputerCraftProxyClient.java +++ b/src/main/java/dan200/computercraft/client/proxy/ComputerCraftProxyClient.java @@ -25,6 +25,7 @@ import dan200.computercraft.shared.pocket.items.ItemPocketComputer; import dan200.computercraft.shared.proxy.ComputerCraftProxyCommon; import dan200.computercraft.shared.turtle.blocks.TileTurtle; import dan200.computercraft.shared.turtle.entity.TurtleVisionCamera; +import dan200.computercraft.shared.util.Colour; import net.minecraft.block.Block; import net.minecraft.client.Minecraft; import net.minecraft.client.renderer.GlStateManager; @@ -53,7 +54,6 @@ import net.minecraftforge.fml.common.eventhandler.SubscribeEvent; import net.minecraftforge.fml.common.gameevent.TickEvent; import net.minecraftforge.fml.relauncher.Side; import net.minecraftforge.fml.relauncher.SideOnly; -import org.lwjgl.opengl.GL11; import java.io.File; import java.util.ArrayList; @@ -117,19 +117,14 @@ public class ComputerCraftProxyClient extends ComputerCraftProxyCommon private ModelResourceLocation pocket_computer_off = new ModelResourceLocation( "computercraft:pocketComputer", "inventory" ); private ModelResourceLocation pocket_computer_on = new ModelResourceLocation( "computercraft:pocket_computer_on", "inventory" ); private ModelResourceLocation pocket_computer_blinking = new ModelResourceLocation( "computercraft:pocket_computer_blinking", "inventory" ); - private ModelResourceLocation pocket_computer_on_modem_on = new ModelResourceLocation( "computercraft:pocket_computer_on_modem_on", "inventory" ); - private ModelResourceLocation pocket_computer_blinking_modem_on = new ModelResourceLocation( "computercraft:pocket_computer_blinking_modem_on", "inventory" ); private ModelResourceLocation advanced_pocket_computer_off = new ModelResourceLocation( "computercraft:advanced_pocket_computer_off", "inventory" ); private ModelResourceLocation advanced_pocket_computer_on = new ModelResourceLocation( "computercraft:advanced_pocket_computer_on", "inventory" ); private ModelResourceLocation advanced_pocket_computer_blinking = new ModelResourceLocation( "computercraft:advanced_pocket_computer_blinking", "inventory" ); - private ModelResourceLocation advanced_pocket_computer_on_modem_on = new ModelResourceLocation( "computercraft:advanced_pocket_computer_on_modem_on", "inventory" ); - private ModelResourceLocation advanced_pocket_computer_blinking_modem_on = new ModelResourceLocation( "computercraft:advanced_pocket_computer_blinking_modem_on", "inventory" ); @Override public ModelResourceLocation getModelLocation( ItemStack stack ) { ItemPocketComputer itemPocketComputer = (ItemPocketComputer)stack.getItem(); - boolean modemOn = itemPocketComputer.getModemState( stack ); switch( itemPocketComputer.getFamily( stack ) ) { case Advanced: @@ -143,11 +138,11 @@ public class ComputerCraftProxyClient extends ComputerCraftProxyCommon } case On: { - return modemOn ? advanced_pocket_computer_on_modem_on : advanced_pocket_computer_on; + return advanced_pocket_computer_on; } case Blinking: { - return modemOn ? advanced_pocket_computer_blinking_modem_on : advanced_pocket_computer_blinking; + return advanced_pocket_computer_blinking; } } } @@ -163,24 +158,36 @@ public class ComputerCraftProxyClient extends ComputerCraftProxyCommon } case On: { - return modemOn ? pocket_computer_on_modem_on : pocket_computer_on; + return pocket_computer_on; } case Blinking: { - return modemOn ? pocket_computer_blinking_modem_on : pocket_computer_blinking; + return pocket_computer_blinking; } } } } } }, new String[] { - "pocketComputer", "pocket_computer_on", "pocket_computer_blinking", "pocket_computer_on_modem_on", "pocket_computer_blinking_modem_on", - "advanced_pocket_computer_off", "advanced_pocket_computer_on", "advanced_pocket_computer_blinking", "advanced_pocket_computer_on_modem_on", "advanced_pocket_computer_blinking_modem_on", + "pocketComputer", "pocket_computer_on", "pocket_computer_blinking", + "advanced_pocket_computer_off", "advanced_pocket_computer_on", "advanced_pocket_computer_blinking", } ); // Setup - mc.getItemColors().registerItemColorHandler(new DiskColorHandler(ComputerCraft.Items.disk), ComputerCraft.Items.disk); - mc.getItemColors().registerItemColorHandler(new DiskColorHandler(ComputerCraft.Items.diskExpanded), ComputerCraft.Items.diskExpanded); + mc.getItemColors().registerItemColorHandler( new DiskColorHandler( ComputerCraft.Items.disk ), ComputerCraft.Items.disk ); + mc.getItemColors().registerItemColorHandler( new DiskColorHandler( ComputerCraft.Items.diskExpanded ), ComputerCraft.Items.diskExpanded ); + + mc.getItemColors().registerItemColorHandler( new IItemColor() + { + @Override + public int getColorFromItemstack( ItemStack stack, int layout ) + { + if( layout != 1 ) return 0xFFFFFF; + + Colour colour = Colour.fromInt( ComputerCraft.Items.pocketComputer.getLightState( stack ) ); + return colour == null ? Colour.Black.getHex() : colour.getHex(); + } + }, ComputerCraft.Items.pocketComputer ); // Setup renderers ClientRegistry.bindTileEntitySpecialRenderer( TileMonitor.class, new TileEntityMonitorRenderer() ); diff --git a/src/main/java/dan200/computercraft/shared/pocket/apis/PocketAPI.java b/src/main/java/dan200/computercraft/shared/pocket/apis/PocketAPI.java index b3d6cf853..8ef404c0f 100644 --- a/src/main/java/dan200/computercraft/shared/pocket/apis/PocketAPI.java +++ b/src/main/java/dan200/computercraft/shared/pocket/apis/PocketAPI.java @@ -6,14 +6,26 @@ package dan200.computercraft.shared.pocket.apis; +import dan200.computercraft.ComputerCraft; import dan200.computercraft.api.lua.ILuaContext; +import dan200.computercraft.api.lua.ILuaTask; import dan200.computercraft.api.lua.LuaException; +import dan200.computercraft.api.pocket.IPocketUpgrade; import dan200.computercraft.core.apis.ILuaAPI; +import dan200.computercraft.shared.pocket.core.PocketServerComputer; +import dan200.computercraft.shared.util.InventoryUtil; +import dan200.computercraft.shared.util.WorldUtil; +import net.minecraft.entity.player.EntityPlayer; +import net.minecraft.entity.player.InventoryPlayer; +import net.minecraft.item.ItemStack; public class PocketAPI implements ILuaAPI { - public PocketAPI() + private final PocketServerComputer m_computer; + + public PocketAPI( PocketServerComputer computer ) { + this.m_computer = computer; } @Override @@ -43,14 +55,123 @@ public class PocketAPI implements ILuaAPI public String[] getMethodNames() { return new String[] { - // TODO: Add some methods + "equipBack", + "unequipBack" }; } @Override - public Object[] callMethod( ILuaContext context, int method, Object[] arguments ) throws LuaException + public Object[] callMethod( ILuaContext context, int method, Object[] arguments ) throws LuaException, InterruptedException { - // TODO: Add some methods + switch( method ) + { + case 0: + // equipBack + return context.executeMainThreadTask( new ILuaTask() + { + @Override + public Object[] execute() throws LuaException + { + if( !(m_computer.getEntity() instanceof EntityPlayer) ) + { + throw new LuaException( "Cannot find player" ); + } + + EntityPlayer player = (EntityPlayer) m_computer.getEntity(); + InventoryPlayer inventory = player.inventory; + + IPocketUpgrade previousUpgrade = m_computer.getUpgrade(); + + // Attempt to find the upgrade, starting in the main segment, and then looking in the opposite + // one. We start from the position the item is currently in and loop round to the start. + IPocketUpgrade newUpgrade = findUpgrade( inventory.mainInventory, inventory.currentItem, previousUpgrade ); + if( newUpgrade == null ) + { + newUpgrade = findUpgrade( inventory.offHandInventory, 0, previousUpgrade ); + } + if( newUpgrade == null ) throw new LuaException( "Cannot find a valid upgrade" ); + + // Remove the current upgrade + if( previousUpgrade != null ) + { + ItemStack stack = previousUpgrade.getCraftingItem(); + if( stack != null ) + { + stack = InventoryUtil.storeItems( stack, inventory, 0, 36, inventory.currentItem ); + if( stack != null ) + { + WorldUtil.dropItemStack( stack, player.worldObj, player.posX, player.posY, player.posZ ); + } + } + } + + // Set the new upgrade + m_computer.setUpgrade( newUpgrade ); + + return null; + } + } ); + + case 1: + // unequipBack + return context.executeMainThreadTask( new ILuaTask() + { + @Override + public Object[] execute() throws LuaException + { + if( !(m_computer.getEntity() instanceof EntityPlayer) ) + { + throw new LuaException( "Cannot find player" ); + } + + EntityPlayer player = (EntityPlayer) m_computer.getEntity(); + InventoryPlayer inventory = player.inventory; + + IPocketUpgrade previousUpgrade = m_computer.getUpgrade(); + + if( previousUpgrade == null ) throw new LuaException( "Nothing to unequip" ); + + m_computer.setUpgrade( null ); + + ItemStack stack = previousUpgrade.getCraftingItem(); + if( stack != null ) + { + stack = InventoryUtil.storeItems( stack, inventory, 0, 36, inventory.currentItem ); + if( stack != null ) + { + WorldUtil.dropItemStack( stack, player.worldObj, player.posX, player.posY, player.posZ ); + } + } + + return null; + } + } ); + default: + return null; + } + } + + private static IPocketUpgrade findUpgrade( ItemStack[] inv, int start, IPocketUpgrade previous ) + { + for (int i = 0; i < inv.length; i++) + { + ItemStack invStack = inv[ (i + start) % inv.length ]; + if( invStack != null ) + { + IPocketUpgrade newUpgrade = ComputerCraft.getPocketUpgrade( invStack ); + + if( newUpgrade != null && newUpgrade != previous ) + { + // Consume an item from this stack and exit the loop + invStack = invStack.copy(); + invStack.stackSize--; + inv[ (i + start) % inv.length ] = invStack.stackSize <= 0 ? null : invStack; + + return newUpgrade; + } + } + } + return null; } } diff --git a/src/main/java/dan200/computercraft/shared/pocket/core/PocketServerComputer.java b/src/main/java/dan200/computercraft/shared/pocket/core/PocketServerComputer.java new file mode 100644 index 000000000..bd0d9470c --- /dev/null +++ b/src/main/java/dan200/computercraft/shared/pocket/core/PocketServerComputer.java @@ -0,0 +1,141 @@ +package dan200.computercraft.shared.pocket.core; + +import dan200.computercraft.ComputerCraft; +import dan200.computercraft.api.peripheral.IPeripheral; +import dan200.computercraft.api.pocket.IPocketAccess; +import dan200.computercraft.api.pocket.IPocketUpgrade; +import dan200.computercraft.shared.computer.core.ComputerFamily; +import dan200.computercraft.shared.computer.core.ServerComputer; +import net.minecraft.entity.Entity; +import net.minecraft.entity.player.EntityPlayer; +import net.minecraft.entity.player.InventoryPlayer; +import net.minecraft.item.ItemStack; +import net.minecraft.nbt.NBTTagCompound; +import net.minecraft.util.ResourceLocation; +import net.minecraft.world.World; + +import javax.annotation.Nonnull; +import javax.annotation.Nullable; +import java.util.Collections; +import java.util.Map; + +public class PocketServerComputer extends ServerComputer implements IPocketAccess +{ + private IPocketUpgrade m_upgrade; + private Entity m_entity; + private ItemStack m_stack; + + public PocketServerComputer( World world, int computerID, String label, int instanceID, ComputerFamily family ) + { + super( world, computerID, label, instanceID, family, ComputerCraft.terminalWidth_pocketComputer, ComputerCraft.terminalHeight_pocketComputer ); + } + + @Nullable + @Override + public Entity getEntity() + { + return m_entity; + } + + @Override + public int getLight() + { + int value = getUserData().getInteger( "modemLight" ); + return value >= 0 && value <= 15 ? value : 0; + } + + @Override + public void setLight( int value ) + { + if( value < 0 || value > 15 ) throw new IllegalArgumentException( "Colour out of bounds" ); + + NBTTagCompound tag = getUserData(); + if( tag.getInteger( "modemLight" ) != value ) + { + tag.setInteger( "modemLight", value ); + updateUserData(); + } + } + + @Nonnull + @Override + public NBTTagCompound getUpgradeNBTData() + { + return ComputerCraft.Items.pocketComputer.getUpgradeInfo( m_stack ); + } + + @Override + public void updateUpgradeNBTData() + { + InventoryPlayer inventory = m_entity instanceof EntityPlayer ? ((EntityPlayer) m_entity).inventory : null; + if( inventory != null ) + { + inventory.markDirty(); + } + } + + @Override + public void invalidatePeripheral() + { + IPeripheral peripheral = m_upgrade == null ? null : m_upgrade.createPeripheral( this ); + setPeripheral( 2, peripheral ); + } + + @Nonnull + @Override + public Map getUpgrades() + { + if( m_upgrade == null ) + { + return Collections.emptyMap(); + } + else + { + return Collections.singletonMap( m_upgrade.getUpgradeID(), getPeripheral( 2 ) ); + } + } + + public IPocketUpgrade getUpgrade() + { + return m_upgrade; + } + + /** + * Set the upgrade for this pocket computer, also updating the item stack. + * + * Note this method is not thread safe - it must be called from the server thread. + * + * @param upgrade The new upgrade to set it to, may be {@code null}. + */ + public void setUpgrade( IPocketUpgrade upgrade ) + { + if( this.m_upgrade == upgrade ) return; + + synchronized (this) + { + ComputerCraft.Items.pocketComputer.setUpgrade( m_stack, upgrade ); + if( m_entity instanceof EntityPlayer ) ((EntityPlayer) m_entity).inventory.markDirty(); + + this.m_upgrade = upgrade; + invalidatePeripheral(); + } + } + + public synchronized void updateValues( Entity entity, ItemStack stack, IPocketUpgrade upgrade ) + { + if( entity != null ) + { + setWorld( entity.getEntityWorld() ); + setPosition( entity.getPosition() ); + } + + m_entity = entity; + m_stack = stack; + + if( this.m_upgrade != upgrade ) + { + this.m_upgrade = upgrade; + invalidatePeripheral(); + } + } +} diff --git a/src/main/java/dan200/computercraft/shared/pocket/items/ItemPocketComputer.java b/src/main/java/dan200/computercraft/shared/pocket/items/ItemPocketComputer.java index 1044cbe37..0199f92b1 100644 --- a/src/main/java/dan200/computercraft/shared/pocket/items/ItemPocketComputer.java +++ b/src/main/java/dan200/computercraft/shared/pocket/items/ItemPocketComputer.java @@ -10,18 +10,17 @@ import com.google.common.base.Objects; import dan200.computercraft.ComputerCraft; import dan200.computercraft.api.filesystem.IMount; import dan200.computercraft.api.media.IMedia; -import dan200.computercraft.api.peripheral.IPeripheral; +import dan200.computercraft.api.pocket.IPocketUpgrade; import dan200.computercraft.shared.computer.blocks.ComputerState; import dan200.computercraft.shared.computer.core.ClientComputer; import dan200.computercraft.shared.computer.core.ComputerFamily; import dan200.computercraft.shared.computer.core.ServerComputer; import dan200.computercraft.shared.computer.items.IComputerItem; import dan200.computercraft.shared.pocket.apis.PocketAPI; -import dan200.computercraft.shared.pocket.peripherals.PocketModemPeripheral; +import dan200.computercraft.shared.pocket.core.PocketServerComputer; import dan200.computercraft.shared.util.StringUtil; import net.minecraft.creativetab.CreativeTabs; import net.minecraft.entity.Entity; -import net.minecraft.entity.EntityLivingBase; import net.minecraft.entity.player.EntityPlayer; import net.minecraft.inventory.IInventory; import net.minecraft.item.Item; @@ -32,11 +31,12 @@ import net.minecraft.util.EnumActionResult; import net.minecraft.util.EnumHand; import net.minecraft.util.SoundEvent; import net.minecraft.world.World; +import net.minecraftforge.common.util.Constants; +import net.minecraftforge.fml.relauncher.Side; +import net.minecraftforge.fml.relauncher.SideOnly; import java.util.List; -; - public class ItemPocketComputer extends Item implements IComputerItem, IMedia { public ItemPocketComputer() @@ -47,7 +47,7 @@ public class ItemPocketComputer extends Item implements IComputerItem, IMedia setCreativeTab( ComputerCraft.mainCreativeTab ); } - public ItemStack create( int id, String label, ComputerFamily family, boolean modem ) + public ItemStack create( int id, String label, ComputerFamily family, IPocketUpgrade upgrade ) { // Ignore types we can't handle if( family != ComputerFamily.Normal && family != ComputerFamily.Advanced ) @@ -58,16 +58,16 @@ public class ItemPocketComputer extends Item implements IComputerItem, IMedia // Build the stack int damage = (family == ComputerFamily.Advanced) ? 1 : 0; ItemStack result = new ItemStack( this, 1, damage ); - if( id >= 0 || modem ) + if( id >= 0 || upgrade != null ) { NBTTagCompound compound = new NBTTagCompound(); if( id >= 0 ) { compound.setInteger( "computerID", id ); } - if( modem ) + if( upgrade != null ) { - compound.setInteger( "upgrade", 1 ); + compound.setString( "upgrade", upgrade.getUpgradeID().toString() ); } result.setTagCompound( compound ); } @@ -79,12 +79,19 @@ public class ItemPocketComputer extends Item implements IComputerItem, IMedia } @Override - public void getSubItems( Item itemID, CreativeTabs tabs, List list ) + public void getSubItems( Item itemID, CreativeTabs tabs, List list ) { - list.add( PocketComputerItemFactory.create( -1, null, ComputerFamily.Normal, false ) ); - list.add( PocketComputerItemFactory.create( -1, null, ComputerFamily.Normal, true ) ); - list.add( PocketComputerItemFactory.create( -1, null, ComputerFamily.Advanced, false ) ); - list.add( PocketComputerItemFactory.create( -1, null, ComputerFamily.Advanced, true ) ); + getSubItems( list, ComputerFamily.Normal ); + getSubItems( list, ComputerFamily.Advanced ); + } + + private void getSubItems( List list, ComputerFamily family ) + { + list.add( PocketComputerItemFactory.create( -1, null, family, null ) ); + for (IPocketUpgrade upgrade : ComputerCraft.getVanillaPocketUpgrades()) + { + list.add( PocketComputerItemFactory.create( -1, null, family, upgrade ) ); + } } @Override @@ -93,13 +100,16 @@ public class ItemPocketComputer extends Item implements IComputerItem, IMedia if( !world.isRemote ) { // Server side - IInventory inventory = (entity instanceof EntityPlayer) ? ((EntityPlayer)entity).inventory : null; - ServerComputer computer = createServerComputer( world, inventory, stack ); + IInventory inventory = (entity instanceof EntityPlayer) ? ((EntityPlayer) entity).inventory : null; + PocketServerComputer computer = createServerComputer( world, inventory, entity, stack ); if( computer != null ) { + IPocketUpgrade upgrade = getUpgrade( stack ); + // Ping computer computer.keepAlive(); computer.setWorld( world ); + computer.updateValues( entity, stack, upgrade ); // Sync ID int id = computer.getID(); @@ -123,30 +133,10 @@ public class ItemPocketComputer extends Item implements IComputerItem, IMedia } } - // Update modem - IPeripheral peripheral = computer.getPeripheral( 2 ); - if( peripheral != null && peripheral instanceof PocketModemPeripheral ) + // Update pocket upgrade + if( upgrade != null ) { - // Location - PocketModemPeripheral modem = (PocketModemPeripheral)peripheral; - if( entity instanceof EntityLivingBase ) - { - EntityLivingBase player = (EntityLivingBase)entity; - modem.setLocation( world, player.posX, player.posY + player.getEyeHeight(), player.posZ ); - } - else - { - modem.setLocation( world, entity.posX, entity.posY, entity.posZ ); - } - - // Light - boolean modemLight = modem.isActive(); - NBTTagCompound modemNBT = computer.getUserData(); - if( modemNBT.getBoolean( "modemLight" ) != modemLight ) - { - modemNBT.setBoolean( "modemLight", modemLight ); - computer.updateUserData(); - } + upgrade.update( computer, computer.getPeripheral( 2 ) ); } } } @@ -166,12 +156,22 @@ public class ItemPocketComputer extends Item implements IComputerItem, IMedia { if( !world.isRemote ) { - ServerComputer computer = createServerComputer( world, player.inventory, stack ); + PocketServerComputer computer = createServerComputer( world, player.inventory, player, stack ); + + boolean stop = false; if( computer != null ) { computer.turnOn(); + + IPocketUpgrade upgrade = getUpgrade( stack ); + if( upgrade != null ) + { + computer.updateValues( player, stack, upgrade ); + stop = upgrade.onRightClick( world, computer, computer.getPeripheral( 2 ) ); + } } - ComputerCraft.openPocketComputerGUI( player, hand ); + + if( !stop ) ComputerCraft.openPocketComputerGUI( player, hand ); } return new ActionResult( EnumActionResult.SUCCESS, stack ); } @@ -197,12 +197,12 @@ public class ItemPocketComputer extends Item implements IComputerItem, IMedia public String getItemStackDisplayName( ItemStack stack ) { String baseString = getUnlocalizedName( stack ); - boolean modem = getHasModem( stack ); - if( modem ) + IPocketUpgrade upgrade = getUpgrade( stack ); + if( upgrade != null ) { return StringUtil.translateToLocalFormatted( baseString + ".upgraded.name", - StringUtil.translateToLocal( "upgrade.computercraft:wireless_modem.adjective" ) + StringUtil.translateToLocal( upgrade.getUnlocalisedAdjective() ) ); } else @@ -224,14 +224,14 @@ public class ItemPocketComputer extends Item implements IComputerItem, IMedia } } - private ServerComputer createServerComputer( final World world, IInventory inventory, ItemStack stack ) + private PocketServerComputer createServerComputer( final World world, IInventory inventory, Entity entity, ItemStack stack ) { if( world.isRemote ) { return null; } - ServerComputer computer; + PocketServerComputer computer; int instanceID = getInstanceID( stack ); int sessionID = getSessionID( stack ); int correctSessionID = ComputerCraft.serverComputerRegistry.getSessionID(); @@ -239,7 +239,7 @@ public class ItemPocketComputer extends Item implements IComputerItem, IMedia if( instanceID >= 0 && sessionID == correctSessionID && ComputerCraft.serverComputerRegistry.contains( instanceID ) ) { - computer = ComputerCraft.serverComputerRegistry.get( instanceID ); + computer = (PocketServerComputer) ComputerCraft.serverComputerRegistry.get( instanceID ); } else { @@ -255,20 +255,15 @@ public class ItemPocketComputer extends Item implements IComputerItem, IMedia computerID = ComputerCraft.createUniqueNumberedSaveDir( world, "computer" ); setComputerID( stack, computerID ); } - computer = new ServerComputer( + computer = new PocketServerComputer( world, computerID, getLabel( stack ), instanceID, - getFamily( stack ), - ComputerCraft.terminalWidth_pocketComputer, - ComputerCraft.terminalHeight_pocketComputer + getFamily( stack ) ); - computer.addAPI( new PocketAPI() ); - if( getHasModem( stack ) ) - { - computer.setPeripheral( 2, new PocketModemPeripheral( false ) ); - } + computer.updateValues( entity, stack, getUpgrade( stack ) ); + computer.addAPI( new PocketAPI( computer ) ); ComputerCraft.serverComputerRegistry.add( instanceID, computer ); if( inventory != null ) { @@ -394,7 +389,7 @@ public class ItemPocketComputer extends Item implements IComputerItem, IMedia @Override public IMount createDataMount( ItemStack stack, World world ) { - ServerComputer computer = createServerComputer( world, null, stack ); + ServerComputer computer = createServerComputer( world, null, null, stack ); if( computer != null ) { return computer.getRootMount(); @@ -440,6 +435,7 @@ public class ItemPocketComputer extends Item implements IComputerItem, IMedia stack.getTagCompound().setInteger( "sessionID", sessionID ); } + @SideOnly(Side.CLIENT) public ComputerState getState( ItemStack stack ) { ClientComputer computer = getClientComputer( stack ); @@ -450,27 +446,79 @@ public class ItemPocketComputer extends Item implements IComputerItem, IMedia return ComputerState.Off; } - public boolean getModemState( ItemStack stack ) + @SideOnly(Side.CLIENT) + public int getLightState( ItemStack stack ) { ClientComputer computer = getClientComputer( stack ); if( computer != null && computer.isOn() ) { NBTTagCompound computerNBT = computer.getUserData(); - if( computerNBT != null && computerNBT.getBoolean( "modemLight" ) ) + if( computerNBT != null ) { - return true; + return computerNBT.getInteger( "modemLight" ); } } - return false; + return 0; } - public boolean getHasModem( ItemStack stack ) + public IPocketUpgrade getUpgrade( ItemStack stack ) { NBTTagCompound compound = stack.getTagCompound(); - if( compound != null && compound.hasKey( "upgrade" ) ) + if( compound != null ) { - return (compound.getInteger( "upgrade" ) == 1); + if( compound.hasKey( "upgrade", Constants.NBT.TAG_STRING ) ) + { + String name = compound.getString( "upgrade" ); + return ComputerCraft.getPocketUpgrade( name ); + } + else if( compound.hasKey( "upgrade", Constants.NBT.TAG_ANY_NUMERIC ) ) + { + int id = compound.getInteger( "upgrade" ); + if( id == 1 ) + { + return ComputerCraft.getPocketUpgrade( "computercraft:wireless_modem" ); + } + } + } + + return null; + } + + public void setUpgrade( ItemStack stack, IPocketUpgrade upgrade ) + { + NBTTagCompound compound = stack.getTagCompound(); + if( compound == null ) stack.setTagCompound( compound = new NBTTagCompound() ); + + if( upgrade == null ) + { + compound.removeTag( "upgrade" ); + } + else + { + compound.setString( "upgrade", upgrade.getUpgradeID().toString() ); + } + + compound.removeTag( "upgrade_info" ); + } + + public NBTTagCompound getUpgradeInfo( ItemStack stack ) + { + NBTTagCompound tag = stack.getTagCompound(); + if( tag == null ) + { + tag = new NBTTagCompound(); + stack.setTagCompound( tag ); + } + + if( tag.hasKey( "upgrade_info", Constants.NBT.TAG_COMPOUND ) ) + { + return tag.getCompoundTag( "upgrade_info" ); + } + else + { + NBTTagCompound sub = new NBTTagCompound(); + tag.setTag( "upgrade_info", sub ); + return sub; } - return false; } } diff --git a/src/main/java/dan200/computercraft/shared/pocket/items/PocketComputerItemFactory.java b/src/main/java/dan200/computercraft/shared/pocket/items/PocketComputerItemFactory.java index 2b17d6555..2fa08d038 100644 --- a/src/main/java/dan200/computercraft/shared/pocket/items/PocketComputerItemFactory.java +++ b/src/main/java/dan200/computercraft/shared/pocket/items/PocketComputerItemFactory.java @@ -7,12 +7,13 @@ package dan200.computercraft.shared.pocket.items; import dan200.computercraft.ComputerCraft; +import dan200.computercraft.api.pocket.IPocketUpgrade; import dan200.computercraft.shared.computer.core.ComputerFamily; import net.minecraft.item.ItemStack; public class PocketComputerItemFactory { - public static ItemStack create( int id, String label, ComputerFamily family, boolean modem ) + public static ItemStack create( int id, String label, ComputerFamily family, IPocketUpgrade upgrade ) { ItemPocketComputer computer = ComputerCraft.Items.pocketComputer; switch( family ) @@ -20,7 +21,7 @@ public class PocketComputerItemFactory case Normal: case Advanced: { - return computer.create( id, label, family, modem ); + return computer.create( id, label, family, upgrade ); } } return null; diff --git a/src/main/java/dan200/computercraft/shared/pocket/peripherals/PocketModem.java b/src/main/java/dan200/computercraft/shared/pocket/peripherals/PocketModem.java new file mode 100644 index 000000000..8d58a8365 --- /dev/null +++ b/src/main/java/dan200/computercraft/shared/pocket/peripherals/PocketModem.java @@ -0,0 +1,89 @@ +package dan200.computercraft.shared.pocket.peripherals; + +import dan200.computercraft.api.peripheral.IPeripheral; +import dan200.computercraft.api.pocket.IPocketAccess; +import dan200.computercraft.api.pocket.IPocketUpgrade; +import dan200.computercraft.shared.peripheral.PeripheralType; +import dan200.computercraft.shared.peripheral.common.PeripheralItemFactory; +import dan200.computercraft.shared.util.Colour; +import net.minecraft.entity.Entity; +import net.minecraft.entity.EntityLivingBase; +import net.minecraft.item.ItemStack; +import net.minecraft.util.ResourceLocation; +import net.minecraft.world.World; + +import javax.annotation.Nonnull; +import javax.annotation.Nullable; + +public class PocketModem implements IPocketUpgrade +{ + private final boolean m_advanced; + + public PocketModem( boolean m_advanced ) + { + this.m_advanced = m_advanced; + } + + @Nonnull + @Override + public ResourceLocation getUpgradeID() + { + return m_advanced + ? new ResourceLocation( "computercraft", "advanved_modem" ) + : new ResourceLocation( "computercraft", "wireless_modem" ); + } + + @Nonnull + @Override + public String getUnlocalisedAdjective() + { + return m_advanced + ? "upgrade.computercraft:advanced_modem.adjective" + : "upgrade.computercraft:wireless_modem.adjective"; + } + + @Nullable + @Override + public ItemStack getCraftingItem() + { + return PeripheralItemFactory.create( + m_advanced ? PeripheralType.AdvancedModem : PeripheralType.WirelessModem, + null, 1 + ); + } + + @Nullable + @Override + public IPeripheral createPeripheral( @Nonnull IPocketAccess access ) + { + return new PocketModemPeripheral( m_advanced ); + } + + @Override + public void update( @Nonnull IPocketAccess access, @Nullable IPeripheral peripheral ) + { + if( peripheral instanceof PocketModemPeripheral ) + { + Entity entity = access.getEntity(); + + PocketModemPeripheral modem = (PocketModemPeripheral) peripheral; + if( entity instanceof EntityLivingBase ) + { + EntityLivingBase player = (EntityLivingBase) entity; + modem.setLocation( entity.getEntityWorld(), player.posX, player.posY + player.getEyeHeight(), player.posZ ); + } + else if( entity != null ) + { + modem.setLocation( entity.getEntityWorld(), entity.posX, entity.posY, entity.posZ ); + } + + access.setLight( modem.isActive() ? Colour.Red.ordinal() : Colour.Black.ordinal() ); + } + } + + @Override + public boolean onRightClick( @Nonnull World world, @Nonnull IPocketAccess access, @Nullable IPeripheral peripheral ) + { + return false; + } +} diff --git a/src/main/java/dan200/computercraft/shared/pocket/recipes/PocketComputerUpgradeRecipe.java b/src/main/java/dan200/computercraft/shared/pocket/recipes/PocketComputerUpgradeRecipe.java index a78623538..7eeefef94 100644 --- a/src/main/java/dan200/computercraft/shared/pocket/recipes/PocketComputerUpgradeRecipe.java +++ b/src/main/java/dan200/computercraft/shared/pocket/recipes/PocketComputerUpgradeRecipe.java @@ -6,9 +6,9 @@ package dan200.computercraft.shared.pocket.recipes; +import dan200.computercraft.ComputerCraft; +import dan200.computercraft.api.pocket.IPocketUpgrade; import dan200.computercraft.shared.computer.core.ComputerFamily; -import dan200.computercraft.shared.peripheral.PeripheralType; -import dan200.computercraft.shared.peripheral.common.IPeripheralItem; import dan200.computercraft.shared.pocket.items.ItemPocketComputer; import dan200.computercraft.shared.pocket.items.PocketComputerItemFactory; import net.minecraft.inventory.InventoryCrafting; @@ -31,7 +31,7 @@ public class PocketComputerUpgradeRecipe implements IRecipe @Override public ItemStack getRecipeOutput() { - return PocketComputerItemFactory.create( -1, null, ComputerFamily.Normal, true ); + return PocketComputerItemFactory.create( -1, null, ComputerFamily.Normal, null ); } @Override @@ -47,9 +47,9 @@ public class PocketComputerUpgradeRecipe implements IRecipe ItemStack computer = null; int computerX = -1; int computerY = -1; - for( int y=0; y