mirror of
https://github.com/MightyPirates/OpenComputers.git
synced 2025-09-13 09:18:05 -04:00
Merge branch 'master-MC1.7.10' of github.com:MightyPirates/OpenComputers into master-MC1.8
Conflicts: src/main/scala/li/cil/oc/client/renderer/item/ItemRenderer.scala src/main/scala/li/cil/oc/client/renderer/tileentity/RobotRenderer.scala src/main/scala/li/cil/oc/common/item/Delegator.scala src/main/scala/li/cil/oc/common/recipe/Recipes.scala src/main/scala/li/cil/oc/integration/opencomputers/DriverFileSystem.scala src/main/scala/li/cil/oc/server/fs/FileSystem.scala
This commit is contained in:
commit
7a0a7e9b3e
@ -16,7 +16,7 @@ import li.cil.oc.api.detail.NetworkAPI;
|
|||||||
*/
|
*/
|
||||||
public class API {
|
public class API {
|
||||||
public static final String ID_OWNER = "OpenComputers|Core";
|
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 DriverAPI driver = null;
|
||||||
public static FileSystemAPI fileSystem = null;
|
public static FileSystemAPI fileSystem = null;
|
||||||
|
@ -123,6 +123,63 @@ public final class FileSystem {
|
|||||||
* access sounds.
|
* access sounds.
|
||||||
* <p/>
|
* <p/>
|
||||||
* The container may be <tt>null</tt>, if no such context can be provided.
|
* The container may be <tt>null</tt>, if no such context can be provided.
|
||||||
|
* <p/>
|
||||||
|
* 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 <tt>null</tt> to create a silent file system.
|
||||||
|
* <p/>
|
||||||
|
* 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.
|
||||||
|
* <tt>opencomputers:floppy_access</tt>.
|
||||||
|
* @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.
|
||||||
|
* <p/>
|
||||||
|
* 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.
|
||||||
|
* <tt>opencomputers:floppy_access</tt>.
|
||||||
|
* @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.
|
||||||
|
* <p/>
|
||||||
|
* 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 fileSystem the file system to wrap.
|
||||||
* @param label the label of the file system.
|
* @param label the label of the file system.
|
||||||
@ -134,14 +191,15 @@ public final class FileSystem {
|
|||||||
* @return the network node wrapping 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, final EnvironmentHost host, final String accessSound) {
|
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 asManagedEnvironment(fileSystem, label, host, accessSound, 1);
|
||||||
return API.fileSystem.asManagedEnvironment(fileSystem, label, host, accessSound);
|
|
||||||
return null;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Like {@link #asManagedEnvironment(li.cil.oc.api.fs.FileSystem, Label, li.cil.oc.api.driver.EnvironmentHost, String)},
|
* Creates a network node that makes the specified file system available via
|
||||||
* but creates a read-only label initialized to the specified value.
|
* the common file system driver.
|
||||||
|
* <p/>
|
||||||
|
* 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 fileSystem the file system to wrap.
|
||||||
* @param label the read-only label of the file system.
|
* @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.
|
* @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) {
|
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 asManagedEnvironment(fileSystem, label, host, accessSound, 1);
|
||||||
return API.fileSystem.asManagedEnvironment(fileSystem, label, host, accessSound);
|
|
||||||
return null;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Like {@link #asManagedEnvironment(li.cil.oc.api.fs.FileSystem, Label, li.cil.oc.api.driver.EnvironmentHost, String)},
|
* Creates a network node that makes the specified file system available via
|
||||||
* but does not provide a container.
|
* the common file system driver.
|
||||||
|
* <p/>
|
||||||
|
* 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 fileSystem the file system to wrap.
|
||||||
* @param label the label of the file system.
|
* @param label the label of the file system.
|
||||||
* @return the network node wrapping 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) {
|
public static ManagedEnvironment asManagedEnvironment(final li.cil.oc.api.fs.FileSystem fileSystem, final Label label) {
|
||||||
if (API.fileSystem != null)
|
return asManagedEnvironment(fileSystem, label, null, null, 1);
|
||||||
return API.fileSystem.asManagedEnvironment(fileSystem, label);
|
|
||||||
return null;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Like {@link #asManagedEnvironment(li.cil.oc.api.fs.FileSystem, Label)},
|
* Creates a network node that makes the specified file system available via
|
||||||
* but creates a read-only label initialized to the specified value.
|
* the common file system driver.
|
||||||
|
* <p/>
|
||||||
|
* 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 fileSystem the file system to wrap.
|
||||||
* @param label the read-only label of the file system.
|
* @param label the read-only label of the file system.
|
||||||
* @return the network node wrapping 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) {
|
public static ManagedEnvironment asManagedEnvironment(final li.cil.oc.api.fs.FileSystem fileSystem, final String label) {
|
||||||
if (API.fileSystem != null)
|
return asManagedEnvironment(fileSystem, label, null, null, 1);
|
||||||
return API.fileSystem.asManagedEnvironment(fileSystem, label);
|
|
||||||
return null;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Like {@link #asManagedEnvironment(li.cil.oc.api.fs.FileSystem, Label)},
|
* Creates a network node that makes the specified file system available via
|
||||||
* but creates an unlabeled file system (i.e. the label can neither be read
|
* the common file system driver.
|
||||||
* nor written).
|
* <p/>
|
||||||
|
* 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.
|
* @param fileSystem the file system to wrap.
|
||||||
* @return the network node wrapping the file system.
|
* @return the network node wrapping the file system.
|
||||||
*/
|
*/
|
||||||
public static ManagedEnvironment asManagedEnvironment(final li.cil.oc.api.fs.FileSystem fileSystem) {
|
public static ManagedEnvironment asManagedEnvironment(final li.cil.oc.api.fs.FileSystem fileSystem) {
|
||||||
if (API.fileSystem != null)
|
return asManagedEnvironment(fileSystem, (Label) null, null, null, 1);
|
||||||
return API.fileSystem.asManagedEnvironment(fileSystem);
|
|
||||||
return null;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// ----------------------------------------------------------------------- //
|
// ----------------------------------------------------------------------- //
|
||||||
|
@ -30,11 +30,11 @@ public interface Keyboard extends Environment, Persistable {
|
|||||||
void setUsableOverride(UsabilityChecker callback);
|
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)
|
* @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.
|
* Whether the specified keyboard is usable by the specified player.
|
||||||
*
|
*
|
||||||
|
@ -563,7 +563,7 @@ public interface TextBuffer extends ManagedEnvironment, Persistable {
|
|||||||
/**
|
/**
|
||||||
* Used when setting a buffer's maximum color depth.
|
* Used when setting a buffer's maximum color depth.
|
||||||
*/
|
*/
|
||||||
public static enum ColorDepth {
|
enum ColorDepth {
|
||||||
/**
|
/**
|
||||||
* Monochrome color, black and white.
|
* Monochrome color, black and white.
|
||||||
*/
|
*/
|
||||||
|
@ -83,56 +83,76 @@ public interface FileSystemAPI {
|
|||||||
* access sounds.
|
* access sounds.
|
||||||
* <p/>
|
* <p/>
|
||||||
* The container may be <tt>null</tt>, if no such context can be provided.
|
* The container may be <tt>null</tt>, if no such context can be provided.
|
||||||
|
* <p/>
|
||||||
|
* 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 <tt>null</tt> to create a silent file system.
|
||||||
|
* <p/>
|
||||||
|
* 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 fileSystem the file system to wrap.
|
||||||
* @param label the label of the file system.
|
* @param label the label of the file system.
|
||||||
* @param host the tile entity containing 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
|
* @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.
|
||||||
|
* <tt>opencomputers:floppy_access</tt>.
|
||||||
|
* @param speed the speed multiplier for this file system.
|
||||||
* @return the network node wrapping the 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)},
|
* Creates a network node that makes the specified file system available via
|
||||||
* but creates a read-only label initialized to the specified value.
|
* the common file system driver.
|
||||||
|
* <p/>
|
||||||
|
* 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 fileSystem the file system to wrap.
|
||||||
* @param label the read-only label of the file system.
|
* @param label the read-only label of the file system.
|
||||||
* @param host the tile entity containing 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
|
* @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.
|
||||||
|
* <tt>opencomputers:floppy_access</tt>.
|
||||||
|
* @param speed the speed multiplier for this file system.
|
||||||
* @return the network node wrapping the 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);
|
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)},
|
* @deprecated Don't use this directly, use the wrapper in {@link li.cil.oc.api.FileSystem}.
|
||||||
* 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
|
||||||
ManagedEnvironment asManagedEnvironment(FileSystem fileSystem, Label label);
|
ManagedEnvironment asManagedEnvironment(FileSystem fileSystem, Label label);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Like {@link #asManagedEnvironment(li.cil.oc.api.fs.FileSystem, Label)},
|
* @deprecated Don't use this directly, use the wrapper in {@link li.cil.oc.api.FileSystem}.
|
||||||
* 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
|
||||||
ManagedEnvironment asManagedEnvironment(FileSystem fileSystem, String label);
|
ManagedEnvironment asManagedEnvironment(FileSystem fileSystem, String label);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Like {@link #asManagedEnvironment(li.cil.oc.api.fs.FileSystem, Label)},
|
* @deprecated Don't use this directly, use the wrapper in {@link li.cil.oc.api.FileSystem}.
|
||||||
* 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
|
||||||
ManagedEnvironment asManagedEnvironment(FileSystem fileSystem);
|
ManagedEnvironment asManagedEnvironment(FileSystem fileSystem);
|
||||||
}
|
}
|
43
src/main/java/li/cil/oc/api/driver/item/UpgradeRenderer.java
Normal file
43
src/main/java/li/cil/oc/api/driver/item/UpgradeRenderer.java
Normal file
@ -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.
|
||||||
|
* <p/>
|
||||||
|
* 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.
|
||||||
|
* <p/>
|
||||||
|
* 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.
|
||||||
|
* <p/>
|
||||||
|
* If the stack cannot be rendered, simply do nothing. This way it's fine
|
||||||
|
* to implement this on a meta item.
|
||||||
|
* <p/>
|
||||||
|
* You usually won't need the robot parameter, but in case you <em>do</em>
|
||||||
|
* 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);
|
||||||
|
}
|
@ -5,7 +5,7 @@ import li.cil.oc.api.Persistable;
|
|||||||
/**
|
/**
|
||||||
* Used by file system components to get and set the file system's label.
|
* 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 {
|
public interface Label extends Persistable {
|
||||||
/**
|
/**
|
||||||
|
@ -1,6 +1,34 @@
|
|||||||
# Do not change this file, it is rewritten each time you start the game.
|
# 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.
|
# 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")
|
include file("hardmode.recipes")
|
||||||
|
|
||||||
analyzer {
|
analyzer {
|
||||||
|
@ -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()
|
||||||
|
}
|
||||||
|
}
|
@ -3,6 +3,7 @@ package li.cil.oc.client.renderer.tileentity
|
|||||||
import com.google.common.base.Strings
|
import com.google.common.base.Strings
|
||||||
import li.cil.oc.OpenComputers
|
import li.cil.oc.OpenComputers
|
||||||
import li.cil.oc.Settings
|
import li.cil.oc.Settings
|
||||||
|
import li.cil.oc.api.driver.item.UpgradeRenderer
|
||||||
import li.cil.oc.api.event.RobotRenderEvent
|
import li.cil.oc.api.event.RobotRenderEvent
|
||||||
import li.cil.oc.client.Textures
|
import li.cil.oc.client.Textures
|
||||||
import li.cil.oc.common.EventHandler
|
import li.cil.oc.common.EventHandler
|
||||||
@ -22,7 +23,6 @@ import net.minecraft.tileentity.TileEntity
|
|||||||
import net.minecraft.util.EnumChatFormatting
|
import net.minecraft.util.EnumChatFormatting
|
||||||
import net.minecraft.util.EnumFacing
|
import net.minecraft.util.EnumFacing
|
||||||
import net.minecraft.util.Vec3
|
import net.minecraft.util.Vec3
|
||||||
import net.minecraftforge.client.IItemRenderer.ItemRenderType
|
|
||||||
import net.minecraftforge.client.MinecraftForgeClient
|
import net.minecraftforge.client.MinecraftForgeClient
|
||||||
import net.minecraftforge.common.MinecraftForge
|
import net.minecraftforge.common.MinecraftForge
|
||||||
import org.lwjgl.opengl.GL11
|
import org.lwjgl.opengl.GL11
|
||||||
@ -123,7 +123,7 @@ object RobotRenderer extends TileEntitySpecialRenderer {
|
|||||||
|
|
||||||
// Back.
|
// Back.
|
||||||
mountPoints(0).offset.setX(0)
|
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).offset.setZ(0.24f)
|
||||||
mountPoints(0).rotation.setX(0)
|
mountPoints(0).rotation.setX(0)
|
||||||
mountPoints(0).rotation.setY(1)
|
mountPoints(0).rotation.setY(1)
|
||||||
@ -131,7 +131,7 @@ object RobotRenderer extends TileEntitySpecialRenderer {
|
|||||||
mountPoints(0).rotation.setW(180)
|
mountPoints(0).rotation.setW(180)
|
||||||
|
|
||||||
mountPoints(1).offset.setX(0)
|
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).offset.setZ(0.24f)
|
||||||
mountPoints(1).rotation.setX(0)
|
mountPoints(1).rotation.setX(0)
|
||||||
mountPoints(1).rotation.setY(1)
|
mountPoints(1).rotation.setY(1)
|
||||||
@ -140,7 +140,7 @@ object RobotRenderer extends TileEntitySpecialRenderer {
|
|||||||
|
|
||||||
// Front.
|
// Front.
|
||||||
mountPoints(2).offset.setX(0)
|
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).offset.setZ(0.24f)
|
||||||
mountPoints(2).rotation.setX(0)
|
mountPoints(2).rotation.setX(0)
|
||||||
mountPoints(2).rotation.setY(1)
|
mountPoints(2).rotation.setY(1)
|
||||||
@ -149,7 +149,7 @@ object RobotRenderer extends TileEntitySpecialRenderer {
|
|||||||
|
|
||||||
// Left.
|
// Left.
|
||||||
mountPoints(3).offset.setX(0)
|
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).offset.setZ(0.24f)
|
||||||
mountPoints(3).rotation.setX(0)
|
mountPoints(3).rotation.setX(0)
|
||||||
mountPoints(3).rotation.setY(1)
|
mountPoints(3).rotation.setY(1)
|
||||||
@ -157,7 +157,7 @@ object RobotRenderer extends TileEntitySpecialRenderer {
|
|||||||
mountPoints(3).rotation.setW(90)
|
mountPoints(3).rotation.setW(90)
|
||||||
|
|
||||||
mountPoints(4).offset.setX(0)
|
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).offset.setZ(0.24f)
|
||||||
mountPoints(4).rotation.setX(0)
|
mountPoints(4).rotation.setX(0)
|
||||||
mountPoints(4).rotation.setY(1)
|
mountPoints(4).rotation.setY(1)
|
||||||
@ -166,7 +166,7 @@ object RobotRenderer extends TileEntitySpecialRenderer {
|
|||||||
|
|
||||||
// Right.
|
// Right.
|
||||||
mountPoints(5).offset.setX(0)
|
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).offset.setZ(0.24f)
|
||||||
mountPoints(5).rotation.setX(0)
|
mountPoints(5).rotation.setX(0)
|
||||||
mountPoints(5).rotation.setY(1)
|
mountPoints(5).rotation.setY(1)
|
||||||
@ -174,7 +174,7 @@ object RobotRenderer extends TileEntitySpecialRenderer {
|
|||||||
mountPoints(5).rotation.setW(-90)
|
mountPoints(5).rotation.setW(-90)
|
||||||
|
|
||||||
mountPoints(6).offset.setX(0)
|
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).offset.setZ(0.24f)
|
||||||
mountPoints(6).rotation.setX(0)
|
mountPoints(6).rotation.setX(0)
|
||||||
mountPoints(6).rotation.setY(1)
|
mountPoints(6).rotation.setY(1)
|
||||||
@ -378,22 +378,17 @@ object RobotRenderer extends TileEntitySpecialRenderer {
|
|||||||
case _ =>
|
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)
|
if (MinecraftForgeClient.getRenderPass == 0) {
|
||||||
for ((stack, mountPoint) <- stacks.zip(mountPoints)) {
|
val stacks = (robot.componentSlots ++ robot.containerSlots).map(robot.getStackInSlot).
|
||||||
try {
|
filter(stack => stack != null && stack.getItem.isInstanceOf[UpgradeRenderer]).
|
||||||
if (stack != null /* && (stack.getItem.requiresMultipleRenderPasses() || MinecraftForgeClient.getRenderPass == 0) TODO remove? */ ) {
|
take(mountPoints.length)
|
||||||
val tint = stack.getItem.getColorFromItemStack(stack, MinecraftForgeClient.getRenderPass)
|
for ((stack, mountPoint) <- stacks.zip(mountPoints.take(stacks.length))) try stack.getItem match {
|
||||||
val r = ((tint >> 16) & 0xFF) / 255f
|
case renderer: UpgradeRenderer =>
|
||||||
val g = ((tint >> 8) & 0xFF) / 255f
|
|
||||||
val b = ((tint >> 0) & 0xFF) / 255f
|
|
||||||
RenderState.color(r, g, b, 1)
|
|
||||||
RenderState.pushMatrix()
|
RenderState.pushMatrix()
|
||||||
GL11.glTranslatef(0.5f, 0.5f, 0.5f)
|
GL11.glTranslatef(0.5f, 0.5f, 0.5f)
|
||||||
GL11.glRotatef(mountPoint.rotation.getW, mountPoint.rotation.getX, mountPoint.rotation.getY, mountPoint.rotation.getZ)
|
renderer.render(stack, mountPoint, robot)
|
||||||
GL11.glTranslatef(mountPoint.offset.getX, mountPoint.offset.getY, mountPoint.offset.getZ)
|
|
||||||
itemRenderer.renderItem(Minecraft.getMinecraft.thePlayer, stack, TransformType.NONE)
|
|
||||||
RenderState.popMatrix()
|
RenderState.popMatrix()
|
||||||
}
|
case _ =>
|
||||||
}
|
}
|
||||||
catch {
|
catch {
|
||||||
case e: Throwable =>
|
case e: Throwable =>
|
||||||
|
@ -82,13 +82,15 @@ class Proxy {
|
|||||||
OpenComputers.channel.register(server.PacketHandler)
|
OpenComputers.channel.register(server.PacketHandler)
|
||||||
|
|
||||||
Loot.init()
|
Loot.init()
|
||||||
Recipes.init()
|
|
||||||
Achievement.init()
|
Achievement.init()
|
||||||
|
|
||||||
EntityRegistry.registerModEntity(classOf[Drone], "Drone", 0, OpenComputers, 80, 1, true)
|
EntityRegistry.registerModEntity(classOf[Drone], "Drone", 0, OpenComputers, 80, 1, true)
|
||||||
|
|
||||||
OpenComputers.log.info("Initializing mod integration.")
|
OpenComputers.log.info("Initializing mod integration.")
|
||||||
Mods.init()
|
Mods.init()
|
||||||
|
|
||||||
|
OpenComputers.log.info("Initializing recipes.")
|
||||||
|
Recipes.init()
|
||||||
}
|
}
|
||||||
|
|
||||||
def postInit(e: FMLPostInitializationEvent) {
|
def postInit(e: FMLPostInitializationEvent) {
|
||||||
|
@ -5,6 +5,10 @@ import java.util.Random
|
|||||||
|
|
||||||
import li.cil.oc.CreativeTab
|
import li.cil.oc.CreativeTab
|
||||||
import li.cil.oc.OpenComputers
|
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.common.tileentity
|
||||||
import li.cil.oc.util.BlockPosition
|
import li.cil.oc.util.BlockPosition
|
||||||
import net.minecraft.client.resources.model.ModelResourceLocation
|
import net.minecraft.client.resources.model.ModelResourceLocation
|
||||||
@ -34,7 +38,7 @@ object Delegator {
|
|||||||
else None
|
else None
|
||||||
}
|
}
|
||||||
|
|
||||||
class Delegator extends Item {
|
class Delegator extends Item with driver.item.UpgradeRenderer {
|
||||||
setHasSubtypes(true)
|
setHasSubtypes(true)
|
||||||
setCreativeTab(CreativeTab)
|
setCreativeTab(CreativeTab)
|
||||||
|
|
||||||
@ -210,4 +214,9 @@ class Delegator extends Item {
|
|||||||
}
|
}
|
||||||
|
|
||||||
override def toString = getUnlocalizedName
|
override def toString = getUnlocalizedName
|
||||||
|
|
||||||
|
// ----------------------------------------------------------------------- //
|
||||||
|
|
||||||
|
@SideOnly(Side.CLIENT)
|
||||||
|
def render(stack: ItemStack, mountPoint: MountPoint, robot: Robot): Unit = UpgradeRenderer.render(stack, mountPoint)
|
||||||
}
|
}
|
||||||
|
@ -126,7 +126,8 @@ object ExtendedRecipe {
|
|||||||
new ItemStack(net.minecraft.init.Blocks.diamond_block)
|
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) {
|
for (stack <- inputs) {
|
||||||
if (beaconBlocks.exists(_.isItemEqual(stack))) {
|
if (beaconBlocks.exists(_.isItemEqual(stack))) {
|
||||||
if (data.isBeaconBase) {
|
if (data.isBeaconBase) {
|
||||||
@ -135,13 +136,20 @@ object ExtendedRecipe {
|
|||||||
}
|
}
|
||||||
data.isBeaconBase = true
|
data.isBeaconBase = true
|
||||||
}
|
}
|
||||||
if (glowstone.isItemEqual(stack)) {
|
if (glowstoneDust.isItemEqual(stack)) {
|
||||||
if (data.lightLevel == 15) {
|
if (data.lightLevel == 15) {
|
||||||
// Crafting wouldn't change anything, prevent accidental resource loss.
|
// Crafting wouldn't change anything, prevent accidental resource loss.
|
||||||
return null
|
return null
|
||||||
}
|
}
|
||||||
data.lightLevel = math.min(15, data.lightLevel + 1)
|
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.
|
// Finally apply modified data.
|
||||||
|
@ -17,9 +17,10 @@ import net.minecraft.block.Block
|
|||||||
import net.minecraft.item.Item
|
import net.minecraft.item.Item
|
||||||
import net.minecraft.item.ItemBlock
|
import net.minecraft.item.ItemBlock
|
||||||
import net.minecraft.item.ItemStack
|
import net.minecraft.item.ItemStack
|
||||||
import net.minecraft.item.crafting.FurnaceRecipes
|
|
||||||
import net.minecraft.nbt.NBTTagCompound
|
import net.minecraft.nbt.NBTTagCompound
|
||||||
import net.minecraft.util.RegistryNamespaced
|
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.Loader
|
||||||
import net.minecraftforge.fml.common.registry.GameRegistry
|
import net.minecraftforge.fml.common.registry.GameRegistry
|
||||||
import net.minecraftforge.oredict.OreDictionary
|
import net.minecraftforge.oredict.OreDictionary
|
||||||
@ -34,6 +35,11 @@ object Recipes {
|
|||||||
val list = mutable.LinkedHashMap.empty[ItemStack, String]
|
val list = mutable.LinkedHashMap.empty[ItemStack, String]
|
||||||
val oreDictEntries = mutable.LinkedHashMap.empty[String, ItemStack]
|
val oreDictEntries = mutable.LinkedHashMap.empty[String, ItemStack]
|
||||||
var hadErrors = false
|
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*) = {
|
def addBlock(instance: Block, name: String, oreDict: String*) = {
|
||||||
Items.registerBlock(instance, name)
|
Items.registerBlock(instance, name)
|
||||||
@ -284,6 +290,16 @@ object Recipes {
|
|||||||
GameRegistry.addRecipe(new ExtendedShapelessOreRecipe(
|
GameRegistry.addRecipe(new ExtendedShapelessOreRecipe(
|
||||||
lightPrint,
|
lightPrint,
|
||||||
print.createItemStack(1), new ItemStack(net.minecraft.init.Items.glowstone_dust)))
|
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 {
|
catch {
|
||||||
case e: Throwable => OpenComputers.log.error("Error parsing recipes, you may not be able to craft any items from this mod!", e)
|
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 {
|
private def addRecipe(output: ItemStack, recipe: Config, name: String) = try {
|
||||||
val recipeType = tryGetType(recipe)
|
val recipeType = tryGetType(recipe)
|
||||||
try {
|
recipeHandlers.get(recipeType) match {
|
||||||
recipeType match {
|
case Some(recipeHandler) => recipeHandler(output, recipe)
|
||||||
case "shaped" => addShapedRecipe(output, recipe)
|
case _ =>
|
||||||
case "shapeless" => addShapelessRecipe(output, recipe)
|
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'.")
|
||||||
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}")
|
|
||||||
hadErrors = true
|
hadErrors = true
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
catch {
|
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)
|
OpenComputers.log.error(s"Failed adding recipe for $name, you will not be able to craft this item.", e)
|
||||||
hadErrors = true
|
hadErrors = true
|
||||||
}
|
}
|
||||||
|
|
||||||
private def addShapedRecipe(output: ItemStack, recipe: Config) {
|
def tryGetCount(recipe: Config) = if (recipe.hasPath("output")) recipe.getInt("output") else 1
|
||||||
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)
|
|
||||||
|
|
||||||
var number = -1
|
def parseIngredient(entry: AnyRef) = entry match {
|
||||||
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 {
|
|
||||||
case map: java.util.Map[AnyRef, AnyRef]@unchecked =>
|
case map: java.util.Map[AnyRef, AnyRef]@unchecked =>
|
||||||
if (map.contains("oreDict")) {
|
if (map.contains("oreDict")) {
|
||||||
map.get("oreDict") match {
|
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")
|
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 {
|
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 item: Item => item.getUnlocalizedName == name || item.getUnlocalizedName == "item." + name || Item.itemRegistry.getNameForObject(item).toString == name
|
||||||
case _ => false
|
case _ => false
|
||||||
@ -483,8 +395,6 @@ object Recipes {
|
|||||||
|
|
||||||
private def tryGetType(recipe: Config) = if (recipe.hasPath("type")) recipe.getString("type") else "shaped"
|
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 =
|
private def tryGetId(ingredient: java.util.Map[AnyRef, AnyRef]): Int =
|
||||||
if (ingredient.contains("subID")) ingredient.get("subID") match {
|
if (ingredient.contains("subID")) ingredient.get("subID") match {
|
||||||
case id: Number => id.intValue
|
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)
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -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())
|
filesystem.foreach(fs => if (fs.node != null) fs.node.remove())
|
||||||
val fs = api.FileSystem.asManagedEnvironment(
|
val fs = api.FileSystem.asManagedEnvironment(
|
||||||
api.FileSystem.fromSaveDirectory(id, wipeDisksAndComputeSpace, Settings.get.bufferChanges),
|
api.FileSystem.fromSaveDirectory(id, wipeDisksAndComputeSpace, Settings.get.bufferChanges),
|
||||||
label, this, Settings.resourceDomain + ":hdd_access").
|
label, this, Settings.resourceDomain + ":hdd_access", 6).
|
||||||
asInstanceOf[FileSystem]
|
asInstanceOf[FileSystem]
|
||||||
val nbtToSetAddress = new NBTTagCompound()
|
val nbtToSetAddress = new NBTTagCompound()
|
||||||
nbtToSetAddress.setString("address", id)
|
nbtToSetAddress.setString("address", id)
|
||||||
|
@ -15,5 +15,7 @@ object ModGregtech extends ModProxy {
|
|||||||
MinecraftForge.EVENT_BUS.register(EventHandlerGregTech)
|
MinecraftForge.EVENT_BUS.register(EventHandlerGregTech)
|
||||||
|
|
||||||
Driver.add(new DriverEnergyContainer)
|
Driver.add(new DriverEnergyContainer)
|
||||||
|
|
||||||
|
RecipeHandler.init()
|
||||||
}
|
}
|
||||||
}
|
}
|
@ -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.")
|
||||||
|
}
|
||||||
|
}
|
@ -24,8 +24,8 @@ object DriverFileSystem extends Item {
|
|||||||
|
|
||||||
override def createEnvironment(stack: ItemStack, host: EnvironmentHost) =
|
override def createEnvironment(stack: ItemStack, host: EnvironmentHost) =
|
||||||
Delegator.subItem(stack) match {
|
Delegator.subItem(stack) match {
|
||||||
case Some(hdd: HardDiskDrive) => createEnvironment(stack, hdd.kiloBytes * 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)
|
case Some(disk: FloppyDisk) => createEnvironment(stack, Settings.get.floppySize * 1024, host, 1)
|
||||||
case _ => null
|
case _ => null
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -42,14 +42,14 @@ object DriverFileSystem extends Item {
|
|||||||
case _ => 0
|
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
|
// 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,
|
// node's address as the folder name... so we generate the address here,
|
||||||
// if necessary. No one will know, right? Right!?
|
// if necessary. No one will know, right? Right!?
|
||||||
val address = addressFromTag(dataTag(stack))
|
val address = addressFromTag(dataTag(stack))
|
||||||
val isFloppy = api.Items.get(stack) == api.Items.get(Constants.ItemName.Floppy)
|
val isFloppy = api.Items.get(stack) == api.Items.get(Constants.ItemName.Floppy)
|
||||||
val fs = oc.api.FileSystem.fromSaveDirectory(address, capacity, Settings.get.bufferChanges)
|
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) {
|
if (environment != null && environment.node != null) {
|
||||||
environment.node.asInstanceOf[oc.server.network.Node].address = address
|
environment.node.asInstanceOf[oc.server.network.Node].address = address
|
||||||
}
|
}
|
||||||
|
@ -34,5 +34,7 @@ object ModVanilla extends ModProxy {
|
|||||||
Driver.add(ConverterNBT)
|
Driver.add(ConverterNBT)
|
||||||
Driver.add(ConverterWorld)
|
Driver.add(ConverterWorld)
|
||||||
Driver.add(ConverterWorldProvider)
|
Driver.add(ConverterWorldProvider)
|
||||||
|
|
||||||
|
RecipeHandler.init()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -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 _ =>
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -23,7 +23,7 @@ import net.minecraftforge.common.util.Constants.NBT
|
|||||||
|
|
||||||
import scala.collection.mutable
|
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).
|
override val node = Network.newNode(this, Visibility.Network).
|
||||||
withComponent("filesystem", Visibility.Neighbors).
|
withComponent("filesystem", Visibility.Neighbors).
|
||||||
withConnector().
|
withConnector().
|
||||||
@ -151,7 +151,6 @@ class FileSystem(val fileSystem: IFileSystem, var label: Label, val host: Option
|
|||||||
result(handle)
|
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 {
|
def read(context: Context, args: Arguments): Array[AnyRef] = fileSystem.synchronized {
|
||||||
val handle = args.checkInteger(0)
|
val handle = args.checkInteger(0)
|
||||||
val n = math.min(Settings.get.maxReadBuffer, math.max(0, args.checkInteger(1)))
|
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 {
|
def seek(context: Context, args: Arguments): Array[AnyRef] = fileSystem.synchronized {
|
||||||
val handle = args.checkInteger(0)
|
val handle = args.checkInteger(0)
|
||||||
val whence = args.checkString(1)
|
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 {
|
def write(context: Context, args: Arguments): Array[AnyRef] = fileSystem.synchronized {
|
||||||
val handle = args.checkInteger(0)
|
val handle = args.checkInteger(0)
|
||||||
val value = args.checkByteArray(1)
|
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)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
@ -96,20 +96,26 @@ object FileSystem extends api.detail.FileSystemAPI {
|
|||||||
|
|
||||||
def fromMemory(capacity: Long): api.fs.FileSystem = new RamFileSystem(capacity)
|
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) =
|
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) =
|
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) =
|
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) =
|
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) =
|
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
|
abstract class ItemLabel(val stack: ItemStack) extends Label
|
||||||
|
|
||||||
|
@ -53,7 +53,7 @@ class Machine(val host: MachineHost) extends prefab.ManagedEnvironment with mach
|
|||||||
|
|
||||||
val tmp = if (Settings.get.tmpSize > 0) {
|
val tmp = if (Settings.get.tmpSize > 0) {
|
||||||
Option(FileSystem.asManagedEnvironment(FileSystem.
|
Option(FileSystem.asManagedEnvironment(FileSystem.
|
||||||
fromMemory(Settings.get.tmpSize * 1024), "tmpfs"))
|
fromMemory(Settings.get.tmpSize * 1024), "tmpfs", null, null, 5))
|
||||||
} else None
|
} else None
|
||||||
|
|
||||||
var architecture: Architecture = _
|
var architecture: Architecture = _
|
||||||
|
Loading…
x
Reference in New Issue
Block a user