diff --git a/src/main/java/li/cil/oc/api/API.java b/src/main/java/li/cil/oc/api/API.java index adbec7d9c..c0981dc2b 100644 --- a/src/main/java/li/cil/oc/api/API.java +++ b/src/main/java/li/cil/oc/api/API.java @@ -16,7 +16,7 @@ import li.cil.oc.api.detail.NetworkAPI; */ public class API { public static final String ID_OWNER = "OpenComputers|Core"; - public static final String VERSION = "5.1.1"; + public static final String VERSION = "5.2.1"; public static DriverAPI driver = null; public static FileSystemAPI fileSystem = null; diff --git a/src/main/java/li/cil/oc/api/FileSystem.java b/src/main/java/li/cil/oc/api/FileSystem.java index 6daac03d9..d4663d5d2 100644 --- a/src/main/java/li/cil/oc/api/FileSystem.java +++ b/src/main/java/li/cil/oc/api/FileSystem.java @@ -123,6 +123,63 @@ public final class FileSystem { * access sounds. *
* The container may be null, if no such context can be provided. + * + * The access sound is the name of the sound effect to play when the file + * system is accessed, for example by listing a directory or reading from + * a file. It may be null to create a silent file system. + * + * The speed multiplier controls how fast read and write operations on the + * file system are. It must be a value in [1,6], and controls the access + * speed, with the default being one. + * For reference, floppies are using the default, hard drives scale with + * their tiers, i.e. a tier one hard drive uses speed two, tier three uses + * speed four. + * + * @param fileSystem the file system to wrap. + * @param label the label of the file system. + * @param host the tile entity containing the file system. + * @param accessSound the name of the sound effect to play when the file + * system is accessed. This has to be the fully + * qualified resource name, e.g. + * opencomputers:floppy_access. + * @param speed the speed multiplier for this file system. + * @return the network node wrapping the file system. + */ + public static ManagedEnvironment asManagedEnvironment(final li.cil.oc.api.fs.FileSystem fileSystem, final Label label, final EnvironmentHost host, final String accessSound, int speed) { + if (API.fileSystem != null) + return API.fileSystem.asManagedEnvironment(fileSystem, label, host, accessSound, speed); + return null; + } + + /** + * Creates a network node that makes the specified file system available via + * the common file system driver. + * + * Creates a file system with the a read-only label and the specified + * access sound and file system speed. + * + * @param fileSystem the file system to wrap. + * @param label the label of the file system. + * @param host the tile entity containing the file system. + * @param accessSound the name of the sound effect to play when the file + * system is accessed. This has to be the fully + * qualified resource name, e.g. + * opencomputers:floppy_access. + * @param speed the speed multiplier for this file system. + * @return the network node wrapping the file system. + */ + public static ManagedEnvironment asManagedEnvironment(final li.cil.oc.api.fs.FileSystem fileSystem, final String label, final EnvironmentHost host, final String accessSound, int speed) { + if (API.fileSystem != null) + return API.fileSystem.asManagedEnvironment(fileSystem, label, host, accessSound, speed); + return null; + } + + /** + * Creates a network node that makes the specified file system available via + * the common file system driver. + * + * Creates a file system with the specified label and the specified access + * sound, using the default file system speed. * * @param fileSystem the file system to wrap. * @param label the label of the file system. @@ -134,14 +191,15 @@ public final class FileSystem { * @return the network node wrapping the file system. */ public static ManagedEnvironment asManagedEnvironment(final li.cil.oc.api.fs.FileSystem fileSystem, final Label label, final EnvironmentHost host, final String accessSound) { - if (API.fileSystem != null) - return API.fileSystem.asManagedEnvironment(fileSystem, label, host, accessSound); - return null; + return asManagedEnvironment(fileSystem, label, host, accessSound, 1); } /** - * Like {@link #asManagedEnvironment(li.cil.oc.api.fs.FileSystem, Label, li.cil.oc.api.driver.EnvironmentHost, String)}, - * but creates a read-only label initialized to the specified value. + * Creates a network node that makes the specified file system available via + * the common file system driver. + * + * Creates a file system with a read-only label and the specified access + * sound, using the default file system speed. * * @param fileSystem the file system to wrap. * @param label the read-only label of the file system. @@ -153,51 +211,52 @@ public final class FileSystem { * @return the network node wrapping the file system. */ public static ManagedEnvironment asManagedEnvironment(final li.cil.oc.api.fs.FileSystem fileSystem, final String label, final EnvironmentHost host, final String accessSound) { - if (API.fileSystem != null) - return API.fileSystem.asManagedEnvironment(fileSystem, label, host, accessSound); - return null; + return asManagedEnvironment(fileSystem, label, host, accessSound, 1); } /** - * Like {@link #asManagedEnvironment(li.cil.oc.api.fs.FileSystem, Label, li.cil.oc.api.driver.EnvironmentHost, String)}, - * but does not provide a container. + * Creates a network node that makes the specified file system available via + * the common file system driver. + * + * Creates a file system with the specified label, without an environment + * and access sound, using the default file system speed. * * @param fileSystem the file system to wrap. * @param label the label of the file system. * @return the network node wrapping the file system. */ public static ManagedEnvironment asManagedEnvironment(final li.cil.oc.api.fs.FileSystem fileSystem, final Label label) { - if (API.fileSystem != null) - return API.fileSystem.asManagedEnvironment(fileSystem, label); - return null; + return asManagedEnvironment(fileSystem, label, null, null, 1); } /** - * Like {@link #asManagedEnvironment(li.cil.oc.api.fs.FileSystem, Label)}, - * but creates a read-only label initialized to the specified value. + * Creates a network node that makes the specified file system available via + * the common file system driver. + * + * Creates a file system with a read-only label, without an environment and + * access sound, using the default file system speed. * * @param fileSystem the file system to wrap. * @param label the read-only label of the file system. * @return the network node wrapping the file system. */ public static ManagedEnvironment asManagedEnvironment(final li.cil.oc.api.fs.FileSystem fileSystem, final String label) { - if (API.fileSystem != null) - return API.fileSystem.asManagedEnvironment(fileSystem, label); - return null; + return asManagedEnvironment(fileSystem, label, null, null, 1); } /** - * Like {@link #asManagedEnvironment(li.cil.oc.api.fs.FileSystem, Label)}, - * but creates an unlabeled file system (i.e. the label can neither be read - * nor written). + * Creates a network node that makes the specified file system available via + * the common file system driver. + * + * Creates an unlabeled file system (i.e. the label can neither be read nor + * written), without an environment and access sound, using the default + * file system speed. * * @param fileSystem the file system to wrap. * @return the network node wrapping the file system. */ public static ManagedEnvironment asManagedEnvironment(final li.cil.oc.api.fs.FileSystem fileSystem) { - if (API.fileSystem != null) - return API.fileSystem.asManagedEnvironment(fileSystem); - return null; + return asManagedEnvironment(fileSystem, (Label) null, null, null, 1); } // ----------------------------------------------------------------------- // diff --git a/src/main/java/li/cil/oc/api/component/Keyboard.java b/src/main/java/li/cil/oc/api/component/Keyboard.java index 1bcbe6bab..a0407be92 100644 --- a/src/main/java/li/cil/oc/api/component/Keyboard.java +++ b/src/main/java/li/cil/oc/api/component/Keyboard.java @@ -30,11 +30,11 @@ public interface Keyboard extends Environment, Persistable { void setUsableOverride(UsabilityChecker callback); /** - * Contract interface that has to implemented for usability check overides. + * Contract interface that has to implemented for usability check overrides. * * @see #setUsableOverride(li.cil.oc.api.component.Keyboard.UsabilityChecker) */ - public static interface UsabilityChecker { + interface UsabilityChecker { /** * Whether the specified keyboard is usable by the specified player. * diff --git a/src/main/java/li/cil/oc/api/component/TextBuffer.java b/src/main/java/li/cil/oc/api/component/TextBuffer.java index 774255ded..45efcd0d2 100644 --- a/src/main/java/li/cil/oc/api/component/TextBuffer.java +++ b/src/main/java/li/cil/oc/api/component/TextBuffer.java @@ -563,7 +563,7 @@ public interface TextBuffer extends ManagedEnvironment, Persistable { /** * Used when setting a buffer's maximum color depth. */ - public static enum ColorDepth { + enum ColorDepth { /** * Monochrome color, black and white. */ diff --git a/src/main/java/li/cil/oc/api/detail/FileSystemAPI.java b/src/main/java/li/cil/oc/api/detail/FileSystemAPI.java index 4ae6459b0..312884f22 100644 --- a/src/main/java/li/cil/oc/api/detail/FileSystemAPI.java +++ b/src/main/java/li/cil/oc/api/detail/FileSystemAPI.java @@ -83,56 +83,76 @@ public interface FileSystemAPI { * access sounds. * * The container may be null, if no such context can be provided. + * + * The access sound is the name of the sound effect to play when the file + * system is accessed, for example by listing a directory or reading from + * a file. It may be null to create a silent file system. + * + * The speed multiplier controls how fast read and write operations on the + * file system are. It must be a value in [1,6], and controls the access + * speed, with the default being one. + * For reference, floppies are using the default, hard drives scale with + * their tiers, i.e. a tier one hard drive uses speed two, tier three uses + * speed four. * * @param fileSystem the file system to wrap. * @param label the label of the file system. * @param host the tile entity containing the file system. * @param accessSound the name of the sound effect to play when the file - * system is accessed. + * system is accessed. This has to be the fully + * qualified resource name, e.g. + * opencomputers:floppy_access. + * @param speed the speed multiplier for this file system. * @return the network node wrapping the file system. */ - ManagedEnvironment asManagedEnvironment(FileSystem fileSystem, Label label, EnvironmentHost host, String accessSound); + ManagedEnvironment asManagedEnvironment(FileSystem fileSystem, Label label, EnvironmentHost host, String accessSound, int speed); /** - * Like {@link #asManagedEnvironment(li.cil.oc.api.fs.FileSystem, Label, li.cil.oc.api.driver.EnvironmentHost, String)}, - * but creates a read-only label initialized to the specified value. + * Creates a network node that makes the specified file system available via + * the common file system driver. + * + * Creates a file system with the a read-only label and the specified + * access sound and file system speed. * * @param fileSystem the file system to wrap. * @param label the read-only label of the file system. * @param host the tile entity containing the file system. * @param accessSound the name of the sound effect to play when the file - * system is accessed. + * system is accessed. This has to be the fully + * qualified resource name, e.g. + * opencomputers:floppy_access. + * @param speed the speed multiplier for this file system. * @return the network node wrapping the file system. */ + ManagedEnvironment asManagedEnvironment(FileSystem fileSystem, String label, EnvironmentHost host, String accessSound, int speed); + + /** + * @deprecated Don't use this directly, use the wrapper in {@link li.cil.oc.api.FileSystem}. + */ + @Deprecated + ManagedEnvironment asManagedEnvironment(FileSystem fileSystem, Label label, EnvironmentHost host, String accessSound); + + /** + * @deprecated Don't use this directly, use the wrapper in {@link li.cil.oc.api.FileSystem}. + */ + @Deprecated ManagedEnvironment asManagedEnvironment(FileSystem fileSystem, String label, EnvironmentHost host, String accessSound); /** - * Like {@link #asManagedEnvironment(li.cil.oc.api.fs.FileSystem, Label, li.cil.oc.api.driver.EnvironmentHost, String)}, - * but does not provide a container and access sound. - * - * @param fileSystem the file system to wrap. - * @param label the label of the file system. - * @return the network node wrapping the file system. + * @deprecated Don't use this directly, use the wrapper in {@link li.cil.oc.api.FileSystem}. */ + @Deprecated ManagedEnvironment asManagedEnvironment(FileSystem fileSystem, Label label); /** - * Like {@link #asManagedEnvironment(li.cil.oc.api.fs.FileSystem, Label)}, - * but creates a read-only label initialized to the specified value. - * - * @param fileSystem the file system to wrap. - * @param label the read-only label of the file system. - * @return the network node wrapping the file system. + * @deprecated Don't use this directly, use the wrapper in {@link li.cil.oc.api.FileSystem}. */ + @Deprecated ManagedEnvironment asManagedEnvironment(FileSystem fileSystem, String label); /** - * Like {@link #asManagedEnvironment(li.cil.oc.api.fs.FileSystem, Label)}, - * but creates an unlabeled file system (i.e. the label can neither be read - * nor written). - * - * @param fileSystem the file system to wrap. - * @return the network node wrapping the file system. + * @deprecated Don't use this directly, use the wrapper in {@link li.cil.oc.api.FileSystem}. */ + @Deprecated ManagedEnvironment asManagedEnvironment(FileSystem fileSystem); } \ No newline at end of file diff --git a/src/main/java/li/cil/oc/api/driver/item/UpgradeRenderer.java b/src/main/java/li/cil/oc/api/driver/item/UpgradeRenderer.java new file mode 100644 index 000000000..313a63428 --- /dev/null +++ b/src/main/java/li/cil/oc/api/driver/item/UpgradeRenderer.java @@ -0,0 +1,43 @@ +package li.cil.oc.api.driver.item; + +import li.cil.oc.api.event.RobotRenderEvent; +import li.cil.oc.api.internal.Robot; +import net.minecraft.item.ItemStack; +import net.minecraftforge.fml.relauncher.Side; +import net.minecraftforge.fml.relauncher.SideOnly; + +/** + * This interface can be implemented by items to allow custom rendering of + * upgrades installed in robots. + * + * Upgrades installed in a robot can have an external representation. This is + * achieved by implementing this interface on an item that serves as a + * renderable upgrade. When the robot is rendered, each equipped upgrade is + * checked for this interface, and if present, the {@link #render} method + * is called. + */ +public interface UpgradeRenderer { + /** + * Render the specified upgrade on a robot. + * + * The GL state has not been adjusted to the mount points position, so + * that you can perform rotations without having to revert the translation. + * It is your responsibility to position the rendered model to fit the + * specified mount point. The state will be such that the origin is the + * center of the robot. This is what the offset of the mount-point is + * relative to. + * + * If the stack cannot be rendered, simply do nothing. This way it's fine + * to implement this on a meta item. + * + * You usually won't need the robot parameter, but in case you do + * need some contextual information, this should provide you with anything + * you could need. + * + * @param stack the item stack of the upgrade to render. + * @param mountPoint the mount-point to render the upgrade at. + * @param robot the robot the upgrade is rendered on. + */ + @SideOnly(Side.CLIENT) + void render(ItemStack stack, RobotRenderEvent.MountPoint mountPoint, Robot robot); +} diff --git a/src/main/java/li/cil/oc/api/fs/Label.java b/src/main/java/li/cil/oc/api/fs/Label.java index e98e45cdb..4256c80ff 100644 --- a/src/main/java/li/cil/oc/api/fs/Label.java +++ b/src/main/java/li/cil/oc/api/fs/Label.java @@ -5,7 +5,7 @@ import li.cil.oc.api.Persistable; /** * Used by file system components to get and set the file system's label. * - * @see li.cil.oc.api.FileSystem#asManagedEnvironment(FileSystem, Label) + * @see li.cil.oc.api.FileSystem#asManagedEnvironment */ public interface Label extends Persistable { /** diff --git a/src/main/resources/assets/opencomputers/recipes/gregtech.recipes b/src/main/resources/assets/opencomputers/recipes/gregtech.recipes index 398be6a8d..d7855fe98 100644 --- a/src/main/resources/assets/opencomputers/recipes/gregtech.recipes +++ b/src/main/resources/assets/opencomputers/recipes/gregtech.recipes @@ -1,6 +1,34 @@ # Do not change this file, it is rewritten each time you start the game. # Instead, use the user.recipes file to edit recipes by redefining them there. +# Note that there is support for a number of GregTech machines, by using the +# appropriate `type` specifier. Available types are: +# - gt_alloySmelter : Alloy Smelter Recipe +# - gt_assembler : Circuit Assembler Machine +# - gt_bender : Plate Bending Machine Recipe +# - gt_canner : Canning Machine Recipe +# - gt_chemical : Chemical Recipe +# - gt_cnc : CNC-Machine Recipe +# - gt_cutter : Cutter Recipe +# - gt_lathe : Lathe Machine Recipe +# - gt_wiremill : Wiremill Recipe +# +# For these types, there a few more options for inputs and outputs. A full +# recipe using all these options would look like this: +# name { +# type: gt_??? +# input: ["primaryInput", "possiblyOptionalSecondaryInput"] +# count: [1, 2] # would mean 1 of primary, 2 of secondary +# output: 2 # size of primary output stack +# eu: EU consumed for the operation +# time: time it takes to complete the operation, in ticks. +# # The following are usually optional. +# secondaryOutput: ["secondaryOutput1", "secondaryOutput2"] # Max number depends on machine. +# secondaryOutputCount: [2, 2] # Like `count` to `input`. +# inputFluid: {name="water", amount="500"} +# outputFluid: {name="lava"} # defaults to amount = 1000 +# } + include file("hardmode.recipes") analyzer { diff --git a/src/main/scala/li/cil/oc/client/renderer/item/UpgradeRenderer.scala b/src/main/scala/li/cil/oc/client/renderer/item/UpgradeRenderer.scala new file mode 100644 index 000000000..6e0440601 --- /dev/null +++ b/src/main/scala/li/cil/oc/client/renderer/item/UpgradeRenderer.scala @@ -0,0 +1,109 @@ +package li.cil.oc.client.renderer.item + +import li.cil.oc.Constants +import li.cil.oc.api +import li.cil.oc.api.event.RobotRenderEvent.MountPoint +import li.cil.oc.client.Textures +import li.cil.oc.integration.opencomputers.Item +import li.cil.oc.util.RenderState +import net.minecraft.client.Minecraft +import net.minecraft.item.ItemStack +import net.minecraft.util.AxisAlignedBB +import org.lwjgl.opengl.GL11 + +object UpgradeRenderer { + lazy val craftingUpgrade = api.Items.get(Constants.ItemName.CraftingUpgrade) + lazy val generatorUpgrade = api.Items.get(Constants.ItemName.GeneratorUpgrade) + lazy val inventoryUpgrade = api.Items.get(Constants.ItemName.InventoryUpgrade) + + def render(stack: ItemStack, mountPoint: MountPoint): Unit = { + val descriptor = api.Items.get(stack) + + if (descriptor == api.Items.get(Constants.ItemName.CraftingUpgrade)) { + Textures.bind(Textures.Model.UpgradeCrafting) + drawSimpleBlock(mountPoint) + + RenderState.checkError(getClass.getName + ".renderItem: crafting upgrade") + } + + else if (descriptor == api.Items.get(Constants.ItemName.GeneratorUpgrade)) { + Textures.bind(Textures.Model.UpgradeGenerator) + drawSimpleBlock(mountPoint, if (Item.dataTag(stack).getInteger("remainingTicks") > 0) 0.5f else 0) + + RenderState.checkError(getClass.getName + ".renderItem: generator upgrade") + } + + else if (descriptor == api.Items.get(Constants.ItemName.InventoryUpgrade)) { + Textures.bind(Textures.Model.UpgradeInventory) + drawSimpleBlock(mountPoint) + + RenderState.checkError(getClass.getName + ".renderItem: inventory upgrade") + } + } + + private val bounds = AxisAlignedBB.fromBounds(-0.1, -0.1, -0.1, 0.1, 0.1, 0.1) + + private def drawSimpleBlock(mountPoint: MountPoint, frontOffset: Float = 0) { + GL11.glRotatef(mountPoint.rotation.getW, mountPoint.rotation.getX, mountPoint.rotation.getY, mountPoint.rotation.getZ) + GL11.glTranslatef(mountPoint.offset.getX, mountPoint.offset.getY, mountPoint.offset.getZ) + + GL11.glBegin(GL11.GL_QUADS) + + // Front. + GL11.glNormal3f(0, 0, 1) + GL11.glTexCoord2f(frontOffset, 0.5f) + GL11.glVertex3d(bounds.minX, bounds.minY, bounds.maxZ) + GL11.glTexCoord2f(frontOffset + 0.5f, 0.5f) + GL11.glVertex3d(bounds.maxX, bounds.minY, bounds.maxZ) + GL11.glTexCoord2f(frontOffset + 0.5f, 0) + GL11.glVertex3d(bounds.maxX, bounds.maxY, bounds.maxZ) + GL11.glTexCoord2f(frontOffset, 0) + GL11.glVertex3d(bounds.minX, bounds.maxY, bounds.maxZ) + + // Top. + GL11.glNormal3f(0, 1, 0) + GL11.glTexCoord2f(1, 0.5f) + GL11.glVertex3d(bounds.maxX, bounds.maxY, bounds.maxZ) + GL11.glTexCoord2f(1, 1) + GL11.glVertex3d(bounds.maxX, bounds.maxY, bounds.minZ) + GL11.glTexCoord2f(0.5f, 1) + GL11.glVertex3d(bounds.minX, bounds.maxY, bounds.minZ) + GL11.glTexCoord2f(0.5f, 0.5f) + GL11.glVertex3d(bounds.minX, bounds.maxY, bounds.maxZ) + + // Bottom. + GL11.glNormal3f(0, -1, 0) + GL11.glTexCoord2f(0.5f, 0.5f) + GL11.glVertex3d(bounds.minX, bounds.minY, bounds.maxZ) + GL11.glTexCoord2f(0.5f, 1) + GL11.glVertex3d(bounds.minX, bounds.minY, bounds.minZ) + GL11.glTexCoord2f(1, 1) + GL11.glVertex3d(bounds.maxX, bounds.minY, bounds.minZ) + GL11.glTexCoord2f(1, 0.5f) + GL11.glVertex3d(bounds.maxX, bounds.minY, bounds.maxZ) + + // Left. + GL11.glNormal3f(1, 0, 0) + GL11.glTexCoord2f(0, 0.5f) + GL11.glVertex3d(bounds.maxX, bounds.maxY, bounds.maxZ) + GL11.glTexCoord2f(0, 1) + GL11.glVertex3d(bounds.maxX, bounds.minY, bounds.maxZ) + GL11.glTexCoord2f(0.5f, 1) + GL11.glVertex3d(bounds.maxX, bounds.minY, bounds.minZ) + GL11.glTexCoord2f(0.5f, 0.5f) + GL11.glVertex3d(bounds.maxX, bounds.maxY, bounds.minZ) + + // Right. + GL11.glNormal3f(-1, 0, 0) + GL11.glTexCoord2f(0, 1) + GL11.glVertex3d(bounds.minX, bounds.minY, bounds.maxZ) + GL11.glTexCoord2f(0, 0.5f) + GL11.glVertex3d(bounds.minX, bounds.maxY, bounds.maxZ) + GL11.glTexCoord2f(0.5f, 0.5f) + GL11.glVertex3d(bounds.minX, bounds.maxY, bounds.minZ) + GL11.glTexCoord2f(0.5f, 1) + GL11.glVertex3d(bounds.minX, bounds.minY, bounds.minZ) + + GL11.glEnd() + } +} diff --git a/src/main/scala/li/cil/oc/client/renderer/tileentity/RobotRenderer.scala b/src/main/scala/li/cil/oc/client/renderer/tileentity/RobotRenderer.scala index 46b9eb28b..449fe7781 100644 --- a/src/main/scala/li/cil/oc/client/renderer/tileentity/RobotRenderer.scala +++ b/src/main/scala/li/cil/oc/client/renderer/tileentity/RobotRenderer.scala @@ -3,6 +3,7 @@ package li.cil.oc.client.renderer.tileentity import com.google.common.base.Strings import li.cil.oc.OpenComputers import li.cil.oc.Settings +import li.cil.oc.api.driver.item.UpgradeRenderer import li.cil.oc.api.event.RobotRenderEvent import li.cil.oc.client.Textures import li.cil.oc.common.EventHandler @@ -22,7 +23,6 @@ import net.minecraft.tileentity.TileEntity import net.minecraft.util.EnumChatFormatting import net.minecraft.util.EnumFacing import net.minecraft.util.Vec3 -import net.minecraftforge.client.IItemRenderer.ItemRenderType import net.minecraftforge.client.MinecraftForgeClient import net.minecraftforge.common.MinecraftForge import org.lwjgl.opengl.GL11 @@ -123,7 +123,7 @@ object RobotRenderer extends TileEntitySpecialRenderer { // Back. mountPoints(0).offset.setX(0) - mountPoints(0).offset.setY(-0.2f) + mountPoints(0).offset.setY(-0.2f - offset) mountPoints(0).offset.setZ(0.24f) mountPoints(0).rotation.setX(0) mountPoints(0).rotation.setY(1) @@ -131,7 +131,7 @@ object RobotRenderer extends TileEntitySpecialRenderer { mountPoints(0).rotation.setW(180) mountPoints(1).offset.setX(0) - mountPoints(1).offset.setY(0.2f + offset) + mountPoints(1).offset.setY(0.2f) mountPoints(1).offset.setZ(0.24f) mountPoints(1).rotation.setX(0) mountPoints(1).rotation.setY(1) @@ -140,7 +140,7 @@ object RobotRenderer extends TileEntitySpecialRenderer { // Front. mountPoints(2).offset.setX(0) - mountPoints(2).offset.setY(-0.2f) + mountPoints(2).offset.setY(-0.2f - offset) mountPoints(2).offset.setZ(0.24f) mountPoints(2).rotation.setX(0) mountPoints(2).rotation.setY(1) @@ -149,7 +149,7 @@ object RobotRenderer extends TileEntitySpecialRenderer { // Left. mountPoints(3).offset.setX(0) - mountPoints(3).offset.setY(-0.2f) + mountPoints(3).offset.setY(-0.2f - offset) mountPoints(3).offset.setZ(0.24f) mountPoints(3).rotation.setX(0) mountPoints(3).rotation.setY(1) @@ -157,7 +157,7 @@ object RobotRenderer extends TileEntitySpecialRenderer { mountPoints(3).rotation.setW(90) mountPoints(4).offset.setX(0) - mountPoints(4).offset.setY(0.2f + offset) + mountPoints(4).offset.setY(0.2f) mountPoints(4).offset.setZ(0.24f) mountPoints(4).rotation.setX(0) mountPoints(4).rotation.setY(1) @@ -166,7 +166,7 @@ object RobotRenderer extends TileEntitySpecialRenderer { // Right. mountPoints(5).offset.setX(0) - mountPoints(5).offset.setY(-0.2f) + mountPoints(5).offset.setY(-0.2f - offset) mountPoints(5).offset.setZ(0.24f) mountPoints(5).rotation.setX(0) mountPoints(5).rotation.setY(1) @@ -174,7 +174,7 @@ object RobotRenderer extends TileEntitySpecialRenderer { mountPoints(5).rotation.setW(-90) mountPoints(6).offset.setX(0) - mountPoints(6).offset.setY(0.2f + offset) + mountPoints(6).offset.setY(0.2f) mountPoints(6).offset.setZ(0.24f) mountPoints(6).rotation.setX(0) mountPoints(6).rotation.setY(1) @@ -378,22 +378,17 @@ object RobotRenderer extends TileEntitySpecialRenderer { case _ => } - val stacks = (robot.componentSlots ++ robot.containerSlots).map(robot.getStackInSlot).filter(stack => stack != null && MinecraftForgeClient.getItemRenderer(stack, ItemRenderType.EQUIPPED) != null).padTo(mountPoints.length, null).take(mountPoints.length) - for ((stack, mountPoint) <- stacks.zip(mountPoints)) { - try { - if (stack != null /* && (stack.getItem.requiresMultipleRenderPasses() || MinecraftForgeClient.getRenderPass == 0) TODO remove? */ ) { - val tint = stack.getItem.getColorFromItemStack(stack, MinecraftForgeClient.getRenderPass) - val r = ((tint >> 16) & 0xFF) / 255f - val g = ((tint >> 8) & 0xFF) / 255f - val b = ((tint >> 0) & 0xFF) / 255f - RenderState.color(r, g, b, 1) + if (MinecraftForgeClient.getRenderPass == 0) { + val stacks = (robot.componentSlots ++ robot.containerSlots).map(robot.getStackInSlot). + filter(stack => stack != null && stack.getItem.isInstanceOf[UpgradeRenderer]). + take(mountPoints.length) + for ((stack, mountPoint) <- stacks.zip(mountPoints.take(stacks.length))) try stack.getItem match { + case renderer: UpgradeRenderer => RenderState.pushMatrix() GL11.glTranslatef(0.5f, 0.5f, 0.5f) - GL11.glRotatef(mountPoint.rotation.getW, mountPoint.rotation.getX, mountPoint.rotation.getY, mountPoint.rotation.getZ) - GL11.glTranslatef(mountPoint.offset.getX, mountPoint.offset.getY, mountPoint.offset.getZ) - itemRenderer.renderItem(Minecraft.getMinecraft.thePlayer, stack, TransformType.NONE) + renderer.render(stack, mountPoint, robot) RenderState.popMatrix() - } + case _ => } catch { case e: Throwable => diff --git a/src/main/scala/li/cil/oc/common/Proxy.scala b/src/main/scala/li/cil/oc/common/Proxy.scala index 899a93dd4..1b507ae87 100644 --- a/src/main/scala/li/cil/oc/common/Proxy.scala +++ b/src/main/scala/li/cil/oc/common/Proxy.scala @@ -82,13 +82,15 @@ class Proxy { OpenComputers.channel.register(server.PacketHandler) Loot.init() - Recipes.init() Achievement.init() EntityRegistry.registerModEntity(classOf[Drone], "Drone", 0, OpenComputers, 80, 1, true) OpenComputers.log.info("Initializing mod integration.") Mods.init() + + OpenComputers.log.info("Initializing recipes.") + Recipes.init() } def postInit(e: FMLPostInitializationEvent) { diff --git a/src/main/scala/li/cil/oc/common/item/Delegator.scala b/src/main/scala/li/cil/oc/common/item/Delegator.scala index 6265cf169..c6696db28 100644 --- a/src/main/scala/li/cil/oc/common/item/Delegator.scala +++ b/src/main/scala/li/cil/oc/common/item/Delegator.scala @@ -5,6 +5,10 @@ import java.util.Random import li.cil.oc.CreativeTab import li.cil.oc.OpenComputers +import li.cil.oc.api.driver +import li.cil.oc.api.event.RobotRenderEvent.MountPoint +import li.cil.oc.api.internal.Robot +import li.cil.oc.client.renderer.item.UpgradeRenderer import li.cil.oc.common.tileentity import li.cil.oc.util.BlockPosition import net.minecraft.client.resources.model.ModelResourceLocation @@ -34,7 +38,7 @@ object Delegator { else None } -class Delegator extends Item { +class Delegator extends Item with driver.item.UpgradeRenderer { setHasSubtypes(true) setCreativeTab(CreativeTab) @@ -210,4 +214,9 @@ class Delegator extends Item { } override def toString = getUnlocalizedName + + // ----------------------------------------------------------------------- // + + @SideOnly(Side.CLIENT) + def render(stack: ItemStack, mountPoint: MountPoint, robot: Robot): Unit = UpgradeRenderer.render(stack, mountPoint) } diff --git a/src/main/scala/li/cil/oc/common/recipe/ExtendedRecipe.scala b/src/main/scala/li/cil/oc/common/recipe/ExtendedRecipe.scala index 250a732c2..4034a6961 100644 --- a/src/main/scala/li/cil/oc/common/recipe/ExtendedRecipe.scala +++ b/src/main/scala/li/cil/oc/common/recipe/ExtendedRecipe.scala @@ -126,7 +126,8 @@ object ExtendedRecipe { new ItemStack(net.minecraft.init.Blocks.diamond_block) ) - val glowstone = new ItemStack(net.minecraft.init.Items.glowstone_dust) + val glowstoneDust = new ItemStack(net.minecraft.init.Items.glowstone_dust) + val glowstone = new ItemStack(net.minecraft.init.Blocks.glowstone) for (stack <- inputs) { if (beaconBlocks.exists(_.isItemEqual(stack))) { if (data.isBeaconBase) { @@ -135,13 +136,20 @@ object ExtendedRecipe { } data.isBeaconBase = true } - if (glowstone.isItemEqual(stack)) { + if (glowstoneDust.isItemEqual(stack)) { if (data.lightLevel == 15) { // Crafting wouldn't change anything, prevent accidental resource loss. return null } data.lightLevel = math.min(15, data.lightLevel + 1) } + if (glowstone.isItemEqual(stack)) { + if (data.lightLevel == 15) { + // Crafting wouldn't change anything, prevent accidental resource loss. + return null + } + data.lightLevel = math.min(15, data.lightLevel + 4) + } } // Finally apply modified data. diff --git a/src/main/scala/li/cil/oc/common/recipe/Recipes.scala b/src/main/scala/li/cil/oc/common/recipe/Recipes.scala index 37032c427..d1846f00a 100644 --- a/src/main/scala/li/cil/oc/common/recipe/Recipes.scala +++ b/src/main/scala/li/cil/oc/common/recipe/Recipes.scala @@ -17,9 +17,10 @@ import net.minecraft.block.Block import net.minecraft.item.Item import net.minecraft.item.ItemBlock import net.minecraft.item.ItemStack -import net.minecraft.item.crafting.FurnaceRecipes import net.minecraft.nbt.NBTTagCompound import net.minecraft.util.RegistryNamespaced +import net.minecraftforge.fluids.FluidRegistry +import net.minecraftforge.fluids.FluidStack import net.minecraftforge.fml.common.Loader import net.minecraftforge.fml.common.registry.GameRegistry import net.minecraftforge.oredict.OreDictionary @@ -34,6 +35,11 @@ object Recipes { val list = mutable.LinkedHashMap.empty[ItemStack, String] val oreDictEntries = mutable.LinkedHashMap.empty[String, ItemStack] var hadErrors = false + val recipeHandlers = mutable.LinkedHashMap.empty[String, (ItemStack, Config) => Unit] + + def registerRecipeHandler(name: String, recipe: (ItemStack, Config) => Unit): Unit = { + recipeHandlers += name -> recipe + } def addBlock(instance: Block, name: String, oreDict: String*) = { Items.registerBlock(instance, name) @@ -284,6 +290,16 @@ object Recipes { GameRegistry.addRecipe(new ExtendedShapelessOreRecipe( lightPrint, print.createItemStack(1), new ItemStack(net.minecraft.init.Items.glowstone_dust))) + + { + val printData = new PrintData(lightPrint) + printData.lightLevel = 4 + printData.save(lightPrint) + } + + GameRegistry.addRecipe(new ExtendedShapelessOreRecipe( + lightPrint, + print.createItemStack(1), new ItemStack(net.minecraft.init.Blocks.glowstone))) } catch { case e: Throwable => OpenComputers.log.error("Error parsing recipes, you may not be able to craft any items from this mod!", e) @@ -293,134 +309,22 @@ object Recipes { private def addRecipe(output: ItemStack, recipe: Config, name: String) = try { val recipeType = tryGetType(recipe) - try { - recipeType match { - case "shaped" => addShapedRecipe(output, recipe) - case "shapeless" => addShapelessRecipe(output, recipe) - case "furnace" => addFurnaceRecipe(output, recipe) - /* TODO GregTech - case "gt_assembler" => - if (Mods.GregTech.isAvailable) { - addGTAssemblingMachineRecipe(output, recipe) - } - else { - OpenComputers.log.error(s"Skipping GregTech assembler recipe for $name because GregTech is not present, you will not be able to craft this item.") - hadErrors = true - } - */ - case other => - OpenComputers.log.error(s"Failed adding recipe for $name, you will not be able to craft this item. The error was: Invalid recipe type '$other'.") - hadErrors = true - } - } - catch { - case e: RecipeException => - OpenComputers.log.error(s"Failed adding $recipeType recipe for $name, you will not be able to craft this item! The error was: ${e.getMessage}") + recipeHandlers.get(recipeType) match { + case Some(recipeHandler) => recipeHandler(output, recipe) + case _ => + OpenComputers.log.error(s"Failed adding recipe for $name, you will not be able to craft this item. The error was: Invalid recipe type '$recipeType'.") hadErrors = true } } catch { - case e: Throwable => + case e: RecipeException => OpenComputers.log.error(s"Failed adding recipe for $name, you will not be able to craft this item.", e) hadErrors = true } - private def addShapedRecipe(output: ItemStack, recipe: Config) { - val rows = recipe.getList("input").unwrapped().map { - case row: java.util.List[AnyRef]@unchecked => row.map(parseIngredient) - case other => throw new RecipeException(s"Invalid row entry for shaped recipe (not a list: $other).") - } - output.stackSize = tryGetCount(recipe) + def tryGetCount(recipe: Config) = if (recipe.hasPath("output")) recipe.getInt("output") else 1 - var number = -1 - var shape = mutable.ArrayBuffer.empty[String] - val input = mutable.ArrayBuffer.empty[AnyRef] - for (row <- rows) { - val (pattern, ingredients) = row.foldLeft((new StringBuilder, Seq.empty[AnyRef]))((acc, ingredient) => { - val (pattern, ingredients) = acc - ingredient match { - case _@(_: ItemStack | _: String) => - number += 1 - (pattern.append(('a' + number).toChar), ingredients ++ Seq(Char.box(('a' + number).toChar), ingredient)) - case _ => (pattern.append(' '), ingredients) - } - }) - shape += pattern.toString - input ++= ingredients - } - if (input.size > 0 && output.stackSize > 0) { - GameRegistry.addRecipe(new ExtendedShapedOreRecipe(output, shape ++ input: _*)) - } - } - - private def addShapelessRecipe(output: ItemStack, recipe: Config) { - val input = recipe.getValue("input").unwrapped() match { - case list: java.util.List[AnyRef]@unchecked => list.map(parseIngredient) - case other => Seq(parseIngredient(other)) - } - output.stackSize = tryGetCount(recipe) - - if (input.size > 0 && output.stackSize > 0) { - GameRegistry.addRecipe(new ExtendedShapelessOreRecipe(output, input: _*)) - } - } - - /* TODO GregTech - private def addGTAssemblingMachineRecipe(output: ItemStack, recipe: Config) { - val inputs = (recipe.getValue("input").unwrapped() match { - case list: java.util.List[AnyRef]@unchecked => list.map(parseIngredient) - case other => Seq(parseIngredient(other)) - }) map { - case null => Array.empty[ItemStack] - case stack: ItemStack => Array(stack) - case name: String => Array(OreDictionary.getOres(name): _*) - case other => throw new RecipeException(s"Invalid ingredient type: $other.") - } - output.stackSize = tryGetCount(recipe) - - if (inputs.size < 1 || inputs.size > 2) { - throw new RecipeException(s"Invalid recipe length: ${inputs.size}, should be 1 or 2.") - } - - val inputCount = recipe.getIntList("count") - if (inputCount.size() != inputs.size) { - throw new RecipeException(s"Ingredient and input count mismatch: ${inputs.size} != ${inputCount.size}.") - } - - val eu = recipe.getInt("eu") - val duration = recipe.getInt("time") - - (inputs, inputCount).zipped.foreach((stacks, count) => stacks.foreach(stack => if (stack != null && count > 0) stack.stackSize = stack.getMaxStackSize min count)) - inputs.padTo(2, null) - - if (inputs.head != null) { - for (input1 <- inputs.head) { - if (inputs.last != null) { - for (input2 <- inputs.last) - gregtech.api.GregTech_API.sRecipeAdder.addAssemblerRecipe(input1, input2, output, duration, eu) - } - else gregtech.api.GregTech_API.sRecipeAdder.addAssemblerRecipe(input1, null, output, duration, eu) - } - } - } - */ - - private def addFurnaceRecipe(output: ItemStack, recipe: Config) { - val input = parseIngredient(recipe.getValue("input").unwrapped()) - output.stackSize = tryGetCount(recipe) - - input match { - case stack: ItemStack => - FurnaceRecipes.instance.addSmeltingRecipe(stack, output, 0) - case name: String => - for (stack <- OreDictionary.getOres(name)) { - FurnaceRecipes.instance.addSmeltingRecipe(stack, output, 0) - } - case _ => - } - } - - private def parseIngredient(entry: AnyRef) = entry match { + def parseIngredient(entry: AnyRef) = entry match { case map: java.util.Map[AnyRef, AnyRef]@unchecked => if (map.contains("oreDict")) { map.get("oreDict") match { @@ -467,6 +371,14 @@ object Recipes { case other => throw new RecipeException(s"Invalid ingredient type (not a map or string): $other") } + def parseFluidIngredient(entry: Config): Option[FluidStack] = { + val fluid = FluidRegistry.getFluid(entry.getString("name")) + val amount = + if (entry.hasPath("amount")) entry.getInt("amount") + else 1000 + Option(new FluidStack(fluid, amount)) + } + private def findItem(name: String) = getObjectWithoutFallback(Item.itemRegistry, name).orElse(Item.itemRegistry.find { case item: Item => item.getUnlocalizedName == name || item.getUnlocalizedName == "item." + name || Item.itemRegistry.getNameForObject(item).toString == name case _ => false @@ -483,8 +395,6 @@ object Recipes { private def tryGetType(recipe: Config) = if (recipe.hasPath("type")) recipe.getString("type") else "shaped" - private def tryGetCount(recipe: Config) = if (recipe.hasPath("output")) recipe.getInt("output") else 1 - private def tryGetId(ingredient: java.util.Map[AnyRef, AnyRef]): Int = if (ingredient.contains("subID")) ingredient.get("subID") match { case id: Number => id.intValue @@ -529,6 +439,6 @@ object Recipes { } } - private class RecipeException(message: String) extends RuntimeException(message) + class RecipeException(message: String) extends RuntimeException(message) } diff --git a/src/main/scala/li/cil/oc/common/tileentity/Raid.scala b/src/main/scala/li/cil/oc/common/tileentity/Raid.scala index 6b92e62f4..cc86452c1 100644 --- a/src/main/scala/li/cil/oc/common/tileentity/Raid.scala +++ b/src/main/scala/li/cil/oc/common/tileentity/Raid.scala @@ -82,7 +82,7 @@ class Raid extends traits.Environment with traits.Inventory with traits.Rotatabl filesystem.foreach(fs => if (fs.node != null) fs.node.remove()) val fs = api.FileSystem.asManagedEnvironment( api.FileSystem.fromSaveDirectory(id, wipeDisksAndComputeSpace, Settings.get.bufferChanges), - label, this, Settings.resourceDomain + ":hdd_access"). + label, this, Settings.resourceDomain + ":hdd_access", 6). asInstanceOf[FileSystem] val nbtToSetAddress = new NBTTagCompound() nbtToSetAddress.setString("address", id) diff --git a/src/main/scala/li/cil/oc/integration/gregtech/ModGregtech.scala b/src/main/scala/li/cil/oc/integration/gregtech/ModGregtech.scala index 4ea66e917..1d8a9afb0 100644 --- a/src/main/scala/li/cil/oc/integration/gregtech/ModGregtech.scala +++ b/src/main/scala/li/cil/oc/integration/gregtech/ModGregtech.scala @@ -15,5 +15,7 @@ object ModGregtech extends ModProxy { MinecraftForge.EVENT_BUS.register(EventHandlerGregTech) Driver.add(new DriverEnergyContainer) + + RecipeHandler.init() } } \ No newline at end of file diff --git a/src/main/scala/li/cil/oc/integration/gregtech/RecipeHandler.scala b/src/main/scala/li/cil/oc/integration/gregtech/RecipeHandler.scala new file mode 100644 index 000000000..42027979b --- /dev/null +++ b/src/main/scala/li/cil/oc/integration/gregtech/RecipeHandler.scala @@ -0,0 +1,188 @@ +package li.cil.oc.integration.gregtech + +import java.util + +import com.typesafe.config.Config +import com.typesafe.config.ConfigValue +import li.cil.oc.common.recipe.Recipes +import li.cil.oc.common.recipe.Recipes.RecipeException +import net.minecraft.item.ItemStack +import net.minecraftforge.fluids.FluidStack +import net.minecraftforge.oredict.OreDictionary + +import scala.collection.convert.WrapAsScala._ + +object RecipeHandler { + def init(): Unit = { + Recipes.registerRecipeHandler("gt_alloySmelter", addGTAlloySmelterRecipe) + Recipes.registerRecipeHandler("gt_assembler", addGTAssemblingMachineRecipe) + Recipes.registerRecipeHandler("gt_bender", addGTBenderRecipe) + Recipes.registerRecipeHandler("gt_canner", addGTCannerRecipe) + Recipes.registerRecipeHandler("gt_chemical", addGTChemicalRecipe) + Recipes.registerRecipeHandler("gt_cnc", addGTCNCRecipe) + Recipes.registerRecipeHandler("gt_cutter", addGTCutterRecipe) + Recipes.registerRecipeHandler("gt_lathe", addGTLatheRecipe) + Recipes.registerRecipeHandler("gt_wiremill", addGTWireMillRecipe) + } + + def addGTAlloySmelterRecipe(output: ItemStack, recipe: Config) { + val (primaryInputs, secondaryInputs, _, _, _, eu, duration) = parseRecipe(output, recipe) + secondaryInputs match { + case Some(value) => + for (primaryInput <- primaryInputs; secondaryInput <- value) { + gregtech.api.GregTech_API.sRecipeAdder.addAlloySmelterRecipe(primaryInput, secondaryInput, output, duration, eu) + } + case _ => + for (primaryInput <- primaryInputs) { + gregtech.api.GregTech_API.sRecipeAdder.addAlloySmelterRecipe(primaryInput, null, output, duration, eu) + } + } + } + + def addGTAssemblingMachineRecipe(output: ItemStack, recipe: Config) { + val (primaryInputs, secondaryInputs, fluidInput, _, _, eu, duration) = parseRecipe(output, recipe) + secondaryInputs match { + case Some(value) => + for (primaryInput <- primaryInputs; secondaryInput <- value) { + gregtech.api.GregTech_API.sRecipeAdder.addAssemblerRecipe(primaryInput, secondaryInput, fluidInput.orNull, output, duration, eu) + } + case _ => + for (primaryInput <- primaryInputs) { + gregtech.api.GregTech_API.sRecipeAdder.addAssemblerRecipe(primaryInput, null, fluidInput.orNull, output, duration, eu) + } + } + } + + def addGTBenderRecipe(output: ItemStack, recipe: Config) { + val (primaryInputs, _, _, _, _, eu, duration) = parseRecipe(output, recipe) + for (primaryInput <- primaryInputs) { + gregtech.api.GregTech_API.sRecipeAdder.addBenderRecipe(primaryInput, output, duration, eu) + } + } + + def addGTCannerRecipe(output: ItemStack, recipe: Config) { + val (primaryInputs, secondaryInputs, _, _, secondaryOutputs, eu, duration) = parseRecipe(output, recipe) + val secondaryOutput = secondaryOutputs.headOption.orNull + secondaryInputs match { + case Some(value) => + for (primaryInput <- primaryInputs; secondaryInput <- value) { + gregtech.api.GregTech_API.sRecipeAdder.addCannerRecipe(primaryInput, secondaryInput, output, secondaryOutput, duration, eu) + } + case None => + for (primaryInput <- primaryInputs) { + gregtech.api.GregTech_API.sRecipeAdder.addCannerRecipe(primaryInput, null, output, secondaryOutput, duration, eu) + } + } + } + + def addGTChemicalRecipe(output: ItemStack, recipe: Config) { + val (primaryInputs, secondaryInputs, fluidInput, fluidOutput, _, _, duration) = parseRecipe(output, recipe) + secondaryInputs match { + case Some(value) => + for (primaryInput <- primaryInputs; secondaryOutput <- value) { + gregtech.api.GregTech_API.sRecipeAdder.addChemicalRecipe(primaryInput, secondaryOutput, fluidInput.orNull, fluidOutput.orNull, output, duration) + } + case _ => + for (primaryInput <- primaryInputs) { + gregtech.api.GregTech_API.sRecipeAdder.addChemicalRecipe(primaryInput, null, fluidInput.orNull, fluidOutput.orNull, output, duration) + } + } + } + + def addGTCNCRecipe(output: ItemStack, recipe: Config) { + val (primaryInputs, _, _, _, _, eu, duration) = parseRecipe(output, recipe) + for (primaryInput <- primaryInputs) { + gregtech.api.GregTech_API.sRecipeAdder.addCNCRecipe(primaryInput, output, duration, eu) + } + } + + def addGTCutterRecipe(output: ItemStack, recipe: Config) { + val (primaryInputs, _, fluidInput, _, secondaryOutputs, eu, duration) = parseRecipe(output, recipe) + val secondaryOutput = secondaryOutputs.headOption.orNull + fluidInput match { + case Some(fluid) => + for (primaryInput <- primaryInputs) { + gregtech.api.GregTech_API.sRecipeAdder.addCutterRecipe(primaryInput, fluid, output, secondaryOutput, duration, eu) + } + case _ => + for (primaryInput <- primaryInputs) { + gregtech.api.GregTech_API.sRecipeAdder.addCutterRecipe(primaryInput, output, secondaryOutput, duration, eu) + } + } + } + + def addGTLatheRecipe(output: ItemStack, recipe: Config) { + val (primaryInputs, _, _, _, secondaryOutputs, eu, duration) = parseRecipe(output, recipe) + val secondaryOutput = secondaryOutputs.headOption.orNull + for (primaryInput <- primaryInputs) { + gregtech.api.GregTech_API.sRecipeAdder.addLatheRecipe(primaryInput, output, secondaryOutput, duration, eu) + } + } + + def addGTWireMillRecipe(output: ItemStack, recipe: Config) { + val (primaryInputs, _, _, _, _, eu, duration) = parseRecipe(output, recipe) + for (primaryInput <- primaryInputs) { + gregtech.api.GregTech_API.sRecipeAdder.addWiremillRecipe(primaryInput, output, duration, eu) + } + } + + private def parseRecipe(output: ItemStack, recipe: Config) = { + val inputs = parseIngredientList(recipe.getValue("input")).toBuffer + output.stackSize = Recipes.tryGetCount(recipe) + + if (inputs.size < 1 || inputs.size > 2) { + throw new RecipeException(s"Invalid recipe length: ${inputs.size}, should be 1 or 2.") + } + + val inputCount = recipe.getIntList("count") + if (inputCount.size() != inputs.size) { + throw new RecipeException(s"Mismatched ingredient count: ${inputs.size} != ${inputCount.size}.") + } + + (inputs, inputCount).zipped.foreach((stacks, count) => + stacks.foreach(stack => + if (stack != null && count > 0) + stack.stackSize = stack.getMaxStackSize min count)) + + inputs.padTo(2, null) + + val outputs = + if (recipe.hasPath("secondaryOutput")) { + val secondaryOutput = parseIngredientList(recipe.getValue("secondaryOutput")).map(_.headOption) + + val outputCount = recipe.getIntList("secondaryOutputCount") + if (outputCount.size() != secondaryOutput.size) { + throw new RecipeException(s"Mismatched secondary output count: ${secondaryOutput.size} != ${outputCount.size}.") + } + + (secondaryOutput, outputCount).zipped.foreach((stack, count) => + if (count > 0) stack.foreach(s => s.stackSize = s.getMaxStackSize min count)) + secondaryOutput.collect { case Some(stack) => stack } + } + else Iterable.empty[ItemStack] + + val inputFluidStack = + if (recipe.hasPath("inputFluid")) Recipes.parseFluidIngredient(recipe.getConfig("inputFluid")) + else None + + val outputFluidStack = + if (recipe.hasPath("outputFluid")) Recipes.parseFluidIngredient(recipe.getConfig("outputFluid")) + else None + + val eu = recipe.getInt("eu") + val duration = recipe.getInt("time") + + (inputs.head, Option(inputs.last), inputFluidStack, outputFluidStack, outputs, eu, duration) + } + + private def parseIngredientList(list: ConfigValue) = + (list.unwrapped() match { + case list: util.List[AnyRef]@unchecked => list.map(Recipes.parseIngredient) + case other => Iterable(Recipes.parseIngredient(other)) + }) map { + case null => Array.empty[ItemStack] + case stack: ItemStack => Array(stack) + case name: String => Array(OreDictionary.getOres(name): _*) + case other => throw new RecipeException(s"Invalid ingredient type: $other.") + } +} diff --git a/src/main/scala/li/cil/oc/integration/opencomputers/DriverFileSystem.scala b/src/main/scala/li/cil/oc/integration/opencomputers/DriverFileSystem.scala index 521c44b86..e1903ac4c 100644 --- a/src/main/scala/li/cil/oc/integration/opencomputers/DriverFileSystem.scala +++ b/src/main/scala/li/cil/oc/integration/opencomputers/DriverFileSystem.scala @@ -24,8 +24,8 @@ object DriverFileSystem extends Item { override def createEnvironment(stack: ItemStack, host: EnvironmentHost) = Delegator.subItem(stack) match { - case Some(hdd: HardDiskDrive) => createEnvironment(stack, hdd.kiloBytes * 1024, host) - case Some(disk: FloppyDisk) => createEnvironment(stack, Settings.get.floppySize * 1024, host) + case Some(hdd: HardDiskDrive) => createEnvironment(stack, hdd.kiloBytes * 1024, host, hdd.tier + 2) + case Some(disk: FloppyDisk) => createEnvironment(stack, Settings.get.floppySize * 1024, host, 1) case _ => null } @@ -42,14 +42,14 @@ object DriverFileSystem extends Item { case _ => 0 } - private def createEnvironment(stack: ItemStack, capacity: Int, host: EnvironmentHost) = if (DimensionManager.getWorld(0) != null) { + private def createEnvironment(stack: ItemStack, capacity: Int, host: EnvironmentHost, speed: Int) = if (DimensionManager.getWorld(0) != null) { // We have a bit of a chicken-egg problem here, because we want to use the // node's address as the folder name... so we generate the address here, // if necessary. No one will know, right? Right!? val address = addressFromTag(dataTag(stack)) val isFloppy = api.Items.get(stack) == api.Items.get(Constants.ItemName.Floppy) val fs = oc.api.FileSystem.fromSaveDirectory(address, capacity, Settings.get.bufferChanges) - val environment = oc.api.FileSystem.asManagedEnvironment(fs, new ReadWriteItemLabel(stack), host, Settings.resourceDomain + ":" + (if (isFloppy) "floppy_access" else "hdd_access")) + val environment = oc.api.FileSystem.asManagedEnvironment(fs, new ReadWriteItemLabel(stack), host, Settings.resourceDomain + ":" + (if (isFloppy) "floppy_access" else "hdd_access"), speed) if (environment != null && environment.node != null) { environment.node.asInstanceOf[oc.server.network.Node].address = address } diff --git a/src/main/scala/li/cil/oc/integration/vanilla/ModVanilla.scala b/src/main/scala/li/cil/oc/integration/vanilla/ModVanilla.scala index d428dc3a1..a1916d1be 100644 --- a/src/main/scala/li/cil/oc/integration/vanilla/ModVanilla.scala +++ b/src/main/scala/li/cil/oc/integration/vanilla/ModVanilla.scala @@ -34,5 +34,7 @@ object ModVanilla extends ModProxy { Driver.add(ConverterNBT) Driver.add(ConverterWorld) Driver.add(ConverterWorldProvider) + + RecipeHandler.init() } } diff --git a/src/main/scala/li/cil/oc/integration/vanilla/RecipeHandler.scala b/src/main/scala/li/cil/oc/integration/vanilla/RecipeHandler.scala new file mode 100644 index 000000000..7318ef24b --- /dev/null +++ b/src/main/scala/li/cil/oc/integration/vanilla/RecipeHandler.scala @@ -0,0 +1,77 @@ +package li.cil.oc.integration.vanilla + +import com.typesafe.config.Config +import li.cil.oc.common.recipe.ExtendedShapedOreRecipe +import li.cil.oc.common.recipe.ExtendedShapelessOreRecipe +import li.cil.oc.common.recipe.Recipes +import li.cil.oc.common.recipe.Recipes.RecipeException +import net.minecraft.item.ItemStack +import net.minecraft.item.crafting.FurnaceRecipes +import net.minecraftforge.fml.common.registry.GameRegistry +import net.minecraftforge.oredict.OreDictionary + +import scala.collection.convert.WrapAsScala._ +import scala.collection.mutable + +object RecipeHandler { + def init(): Unit = { + Recipes.registerRecipeHandler("shaped", addShapedRecipe) + Recipes.registerRecipeHandler("shapeless", addShapelessRecipe) + Recipes.registerRecipeHandler("furnace", addFurnaceRecipe) + } + + def addShapedRecipe(output: ItemStack, recipe: Config) { + val rows = recipe.getList("input").unwrapped().map { + case row: java.util.List[AnyRef]@unchecked => row.map(Recipes.parseIngredient) + case other => throw new RecipeException(s"Invalid row entry for shaped recipe (not a list: $other).") + } + output.stackSize = Recipes.tryGetCount(recipe) + + var number = -1 + var shape = mutable.ArrayBuffer.empty[String] + val input = mutable.ArrayBuffer.empty[AnyRef] + for (row <- rows) { + val (pattern, ingredients) = row.foldLeft((new StringBuilder, Iterable.empty[AnyRef]))((acc, ingredient) => { + val (pattern, ingredients) = acc + ingredient match { + case _@(_: ItemStack | _: String) => + number += 1 + (pattern.append(('a' + number).toChar), ingredients ++ Iterable(Char.box(('a' + number).toChar), ingredient)) + case _ => (pattern.append(' '), ingredients) + } + }) + shape += pattern.toString + input ++= ingredients + } + if (input.size > 0 && output.stackSize > 0) { + GameRegistry.addRecipe(new ExtendedShapedOreRecipe(output, shape ++ input: _*)) + } + } + + def addShapelessRecipe(output: ItemStack, recipe: Config) { + val input = recipe.getValue("input").unwrapped() match { + case list: java.util.List[AnyRef]@unchecked => list.map(Recipes.parseIngredient) + case other => Seq(Recipes.parseIngredient(other)) + } + output.stackSize = Recipes.tryGetCount(recipe) + + if (input.size > 0 && output.stackSize > 0) { + GameRegistry.addRecipe(new ExtendedShapelessOreRecipe(output, input: _*)) + } + } + + def addFurnaceRecipe(output: ItemStack, recipe: Config) { + val input = Recipes.parseIngredient(recipe.getValue("input").unwrapped()) + output.stackSize = Recipes.tryGetCount(recipe) + + input match { + case stack: ItemStack => + FurnaceRecipes.instance.addSmeltingRecipe(stack, output, 0) + case name: String => + for (stack <- OreDictionary.getOres(name)) { + FurnaceRecipes.instance.addSmeltingRecipe(stack, output, 0) + } + case _ => + } + } +} diff --git a/src/main/scala/li/cil/oc/server/component/FileSystem.scala b/src/main/scala/li/cil/oc/server/component/FileSystem.scala index 552b7d14b..10d6e3487 100644 --- a/src/main/scala/li/cil/oc/server/component/FileSystem.scala +++ b/src/main/scala/li/cil/oc/server/component/FileSystem.scala @@ -23,7 +23,7 @@ import net.minecraftforge.common.util.Constants.NBT import scala.collection.mutable -class FileSystem(val fileSystem: IFileSystem, var label: Label, val host: Option[EnvironmentHost] = None, val sound: Option[String] = None) extends prefab.ManagedEnvironment { +class FileSystem(val fileSystem: IFileSystem, var label: Label, val host: Option[EnvironmentHost], val sound: Option[String]) extends prefab.ManagedEnvironment { override val node = Network.newNode(this, Visibility.Network). withComponent("filesystem", Visibility.Neighbors). withConnector(). @@ -151,7 +151,6 @@ class FileSystem(val fileSystem: IFileSystem, var label: Label, val host: Option result(handle) } - @Callback(direct = true, limit = 4, doc = """function(handle:number, count:number):string or nil -- Reads up to the specified amount of data from an open file descriptor with the specified handle. Returns nil when EOF is reached.""") def read(context: Context, args: Arguments): Array[AnyRef] = fileSystem.synchronized { val handle = args.checkInteger(0) val n = math.min(Settings.get.maxReadBuffer, math.max(0, args.checkInteger(1))) @@ -183,7 +182,6 @@ class FileSystem(val fileSystem: IFileSystem, var label: Label, val host: Option } } - @Callback(direct = true, limit = 4, doc = """function(handle:number, whence:string, offset:number):number -- Seeks in an open file descriptor with the specified handle. Returns the new pointer position.""") def seek(context: Context, args: Arguments): Array[AnyRef] = fileSystem.synchronized { val handle = args.checkInteger(0) val whence = args.checkString(1) @@ -202,7 +200,6 @@ class FileSystem(val fileSystem: IFileSystem, var label: Label, val host: Option } } - @Callback(doc = """function(handle:number, value:string):boolean -- Writes the specified data to an open file descriptor with the specified handle.""") def write(context: Context, args: Arguments): Array[AnyRef] = fileSystem.synchronized { val handle = args.checkInteger(0) val value = args.checkByteArray(1) @@ -315,3 +312,69 @@ class FileSystem(val fileSystem: IFileSystem, var label: Label, val host: Option } } } + +object FileSystem { + // I really need to come up with a way to make the call limit dynamic... + def apply(fileSystem: IFileSystem, label: Label, host: Option[EnvironmentHost], sound: Option[String], speed: Int = 1): FileSystem = speed match { + case 6 => new FileSystem(fileSystem, label, host, sound) { + @Callback(direct = true, limit = 15, doc = """function(handle:number, count:number):string or nil -- Reads up to the specified amount of data from an open file descriptor with the specified handle. Returns nil when EOF is reached.""") + override def read(context: Context, args: Arguments): Array[AnyRef] = super.read(context, args) + + @Callback(direct = true, limit = 15, doc = """function(handle:number, whence:string, offset:number):number -- Seeks in an open file descriptor with the specified handle. Returns the new pointer position.""") + override def seek(context: Context, args: Arguments): Array[AnyRef] = super.seek(context, args) + + @Callback(direct = true, limit = 6, doc = """function(handle:number, value:string):boolean -- Writes the specified data to an open file descriptor with the specified handle.""") + override def write(context: Context, args: Arguments): Array[AnyRef] = super.write(context, args) + } + case 5 => new FileSystem(fileSystem, label, host, sound) { + @Callback(direct = true, limit = 13, doc = """function(handle:number, count:number):string or nil -- Reads up to the specified amount of data from an open file descriptor with the specified handle. Returns nil when EOF is reached.""") + override def read(context: Context, args: Arguments): Array[AnyRef] = super.read(context, args) + + @Callback(direct = true, limit = 13, doc = """function(handle:number, whence:string, offset:number):number -- Seeks in an open file descriptor with the specified handle. Returns the new pointer position.""") + override def seek(context: Context, args: Arguments): Array[AnyRef] = super.seek(context, args) + + @Callback(direct = true, limit = 5, doc = """function(handle:number, value:string):boolean -- Writes the specified data to an open file descriptor with the specified handle.""") + override def write(context: Context, args: Arguments): Array[AnyRef] = super.write(context, args) + } + case 4 => new FileSystem(fileSystem, label, host, sound) { + @Callback(direct = true, limit = 10, doc = """function(handle:number, count:number):string or nil -- Reads up to the specified amount of data from an open file descriptor with the specified handle. Returns nil when EOF is reached.""") + override def read(context: Context, args: Arguments): Array[AnyRef] = super.read(context, args) + + @Callback(direct = true, limit = 10, doc = """function(handle:number, whence:string, offset:number):number -- Seeks in an open file descriptor with the specified handle. Returns the new pointer position.""") + override def seek(context: Context, args: Arguments): Array[AnyRef] = super.seek(context, args) + + @Callback(direct = true, limit = 4, doc = """function(handle:number, value:string):boolean -- Writes the specified data to an open file descriptor with the specified handle.""") + override def write(context: Context, args: Arguments): Array[AnyRef] = super.write(context, args) + } + case 3 => new FileSystem(fileSystem, label, host, sound) { + @Callback(direct = true, limit = 7, doc = """function(handle:number, count:number):string or nil -- Reads up to the specified amount of data from an open file descriptor with the specified handle. Returns nil when EOF is reached.""") + override def read(context: Context, args: Arguments): Array[AnyRef] = super.read(context, args) + + @Callback(direct = true, limit = 7, doc = """function(handle:number, whence:string, offset:number):number -- Seeks in an open file descriptor with the specified handle. Returns the new pointer position.""") + override def seek(context: Context, args: Arguments): Array[AnyRef] = super.seek(context, args) + + @Callback(direct = true, limit = 3, doc = """function(handle:number, value:string):boolean -- Writes the specified data to an open file descriptor with the specified handle.""") + override def write(context: Context, args: Arguments): Array[AnyRef] = super.write(context, args) + } + case 2 => new FileSystem(fileSystem, label, host, sound) { + @Callback(direct = true, limit = 4, doc = """function(handle:number, count:number):string or nil -- Reads up to the specified amount of data from an open file descriptor with the specified handle. Returns nil when EOF is reached.""") + override def read(context: Context, args: Arguments): Array[AnyRef] = super.read(context, args) + + @Callback(direct = true, limit = 4, doc = """function(handle:number, whence:string, offset:number):number -- Seeks in an open file descriptor with the specified handle. Returns the new pointer position.""") + override def seek(context: Context, args: Arguments): Array[AnyRef] = super.seek(context, args) + + @Callback(direct = true, limit = 2, doc = """function(handle:number, value:string):boolean -- Writes the specified data to an open file descriptor with the specified handle.""") + override def write(context: Context, args: Arguments): Array[AnyRef] = super.write(context, args) + } + case _ => new FileSystem(fileSystem, label, host, sound) { + @Callback(direct = true, limit = 1, doc = """function(handle:number, count:number):string or nil -- Reads up to the specified amount of data from an open file descriptor with the specified handle. Returns nil when EOF is reached.""") + override def read(context: Context, args: Arguments): Array[AnyRef] = super.read(context, args) + + @Callback(direct = true, limit = 1, doc = """function(handle:number, whence:string, offset:number):number -- Seeks in an open file descriptor with the specified handle. Returns the new pointer position.""") + override def seek(context: Context, args: Arguments): Array[AnyRef] = super.seek(context, args) + + @Callback(direct = true, limit = 1, doc = """function(handle:number, value:string):boolean -- Writes the specified data to an open file descriptor with the specified handle.""") + override def write(context: Context, args: Arguments): Array[AnyRef] = super.write(context, args) + } + } +} diff --git a/src/main/scala/li/cil/oc/server/fs/FileSystem.scala b/src/main/scala/li/cil/oc/server/fs/FileSystem.scala index 0b8f055a1..5d1d95fda 100644 --- a/src/main/scala/li/cil/oc/server/fs/FileSystem.scala +++ b/src/main/scala/li/cil/oc/server/fs/FileSystem.scala @@ -96,20 +96,26 @@ object FileSystem extends api.detail.FileSystemAPI { def fromMemory(capacity: Long): api.fs.FileSystem = new RamFileSystem(capacity) + def asManagedEnvironment(fileSystem: api.fs.FileSystem, label: Label, host: EnvironmentHost, accessSound: String, speed: Int) = + Option(fileSystem).flatMap(fs => Some(component.FileSystem(fs, label, Option(host), Option(accessSound), speed))).orNull + + def asManagedEnvironment(fileSystem: api.fs.FileSystem, label: String, host: EnvironmentHost, accessSound: String, speed: Int) = + asManagedEnvironment(fileSystem, new ReadOnlyLabel(label), host, accessSound, speed) + def asManagedEnvironment(fileSystem: api.fs.FileSystem, label: Label, host: EnvironmentHost, sound: String) = - Option(fileSystem).flatMap(fs => Some(new component.FileSystem(fs, label, Option(host), Option(sound)))).orNull + asManagedEnvironment(fileSystem, label, host, sound, 1) def asManagedEnvironment(fileSystem: api.fs.FileSystem, label: String, host: EnvironmentHost, sound: String) = - asManagedEnvironment(fileSystem, new ReadOnlyLabel(label), host, sound) + asManagedEnvironment(fileSystem, new ReadOnlyLabel(label), host, sound, 1) def asManagedEnvironment(fileSystem: api.fs.FileSystem, label: Label) = - Option(fileSystem).flatMap(fs => Some(new component.FileSystem(fs, label))).orNull + asManagedEnvironment(fileSystem, label, null, null, 1) def asManagedEnvironment(fileSystem: api.fs.FileSystem, label: String) = - asManagedEnvironment(fileSystem, new ReadOnlyLabel(label)) + asManagedEnvironment(fileSystem, new ReadOnlyLabel(label), null, null, 1) def asManagedEnvironment(fileSystem: api.fs.FileSystem) = - asManagedEnvironment(fileSystem, null: Label) + asManagedEnvironment(fileSystem, null: Label, null, null, 1) abstract class ItemLabel(val stack: ItemStack) extends Label diff --git a/src/main/scala/li/cil/oc/server/machine/Machine.scala b/src/main/scala/li/cil/oc/server/machine/Machine.scala index 8a6b92fc8..efa9befc8 100644 --- a/src/main/scala/li/cil/oc/server/machine/Machine.scala +++ b/src/main/scala/li/cil/oc/server/machine/Machine.scala @@ -53,7 +53,7 @@ class Machine(val host: MachineHost) extends prefab.ManagedEnvironment with mach val tmp = if (Settings.get.tmpSize > 0) { Option(FileSystem.asManagedEnvironment(FileSystem. - fromMemory(Settings.get.tmpSize * 1024), "tmpfs")) + fromMemory(Settings.get.tmpSize * 1024), "tmpfs", null, null, 5)) } else None var architecture: Architecture = _