package gtPlusPlus.xmod.gregtech.api.objects; import java.util.HashMap; import gregtech.api.GregTech_API; import gregtech.api.metatileentity.implementations.GT_MetaTileEntity_Hatch; import gregtech.api.metatileentity.implementations.GT_MetaTileEntity_Hatch_Dynamo; import gregtech.api.metatileentity.implementations.GT_MetaTileEntity_Hatch_Energy; import gregtech.api.metatileentity.implementations.GT_MetaTileEntity_Hatch_Input; import gregtech.api.metatileentity.implementations.GT_MetaTileEntity_Hatch_InputBus; import gregtech.api.metatileentity.implementations.GT_MetaTileEntity_Hatch_Maintenance; import gregtech.api.metatileentity.implementations.GT_MetaTileEntity_Hatch_Muffler; import gregtech.api.metatileentity.implementations.GT_MetaTileEntity_Hatch_Output; import gregtech.api.metatileentity.implementations.GT_MetaTileEntity_Hatch_OutputBus; import gregtech.api.metatileentity.implementations.GT_MetaTileEntity_MultiBlockBase; import gtPlusPlus.api.objects.Logger; import gtPlusPlus.api.objects.data.AutoMap; import gtPlusPlus.api.objects.data.Pair; import gtPlusPlus.core.lib.CORE; import gtPlusPlus.core.util.reflect.ReflectionUtils; import net.minecraft.block.Block; import net.minecraft.block.BlockAir; import net.minecraft.init.Blocks; import net.minecraftforge.common.util.ForgeDirection; public class MultiblockLayer { public final int width; public final int depth; private boolean mFinalised; /** * WARNING!! May be {@link null}. */ private Pair mControllerLocation; /** * Holds the North facing Orientation data. */ public final LayerBlockData[][] mLayerData; public final AutoMap mVariantOrientations = new AutoMap(); /** * A detailed class which will contain a single x/z Layer for a {@link MultiblockBlueprint}. * Values are not relative, but in total. * @param x - Overall width * @param z - Overall depth */ public MultiblockLayer(final int x, final int z) { width = x; depth = z; mLayerData = new LayerBlockData[x][z]; //Logger.INFO("Created new Blueprint Layer."); } /** * A detailed class which will contain a single x/z Layer for a {@link MultiblockBlueprint}. * Values are not relative, but in total. */ public MultiblockLayer(final LayerBlockData[][] aData) { width = aData.length; depth = aData[0].length; mLayerData = aData; } /** * * @param aBlock - The block expected as this location. * @param aMeta - The meta for the block above. * @param x - The X location, where 0 is the top left corner & counts upwards, moving right. * @param z - The Z location, where 0 is the top left corner & counts upwards, moving down. * @param canBeHatch - is this location able to be substituted for any hatch? * @return - Is this data added to the layer data map? */ public boolean addBlockForPos(Block aBlock, int aMeta, int x, int z, boolean canBeHatch) { return addBlockForPos(aBlock, aMeta, x, z, canBeHatch, new Class[] {}); } /** * * @param aBlock - The block expected as this location. * @param aMeta - The meta for the block above. * @param x - The X location, where 0 is the top left corner & counts upwards, moving right. * @param z - The Z location, where 0 is the top left corner & counts upwards, moving down. * @param canBeHatch - is this location able to be substituted for a hatch? * @param aHatchTypeClass - Specify the class for the hatch if you want it explicit. Use base classes to allow custom hatches which extend. * @return - Is this data added to the layer data map? */ public boolean addBlockForPos(Block aBlock, int aMeta, int x, int z, boolean canBeHatch, Class aHatchTypeClass) { return addBlockForPos(aBlock, aMeta, x, z, canBeHatch, new Class[] {aHatchTypeClass}); } /** * * @param aBlock - The block expected as this location. * @param aMeta - The meta for the block above. * @param x - The X location, where 0 is the top left corner & counts upwards, moving right. * @param z - The Z location, where 0 is the top left corner & counts upwards, moving down. * @param canBeHatch - is this location able to be substituted for a hatch? * @param aHatchTypeClass - Specify the class for the hatch if you want it explicit. Use base classes to allow custom hatches which extend. * @return - Is this data added to the layer data map? */ public boolean addBlockForPos(Block aBlock, int aMeta, int x, int z, boolean canBeHatch, Class[] aHatchTypeClass) { if (x > width -1) { return false; } if (z > depth - 1) { return false; } if (canBeHatch && (aHatchTypeClass == null || aHatchTypeClass.length <= 0)){ if (!CORE.MAIN_GREGTECH_5U_EXPERIMENTAL_FORK) { aHatchTypeClass = new Class[] { GT_MetaTileEntity_Hatch_Dynamo.class, GT_MetaTileEntity_Hatch_Energy.class, GT_MetaTileEntity_Hatch_Input.class, GT_MetaTileEntity_Hatch_InputBus.class, GT_MetaTileEntity_Hatch_Maintenance.class, GT_MetaTileEntity_Hatch_Muffler.class, GT_MetaTileEntity_Hatch_Output.class, GT_MetaTileEntity_Hatch_OutputBus.class, GT_MetaTileEntity_Hatch.class }; } else { Class aDataHatch = ReflectionUtils.getClass("gregtech.api.metatileentity.implementations.GT_MetaTileEntity_Hatch_DataAccess"); if (aDataHatch != null) { aHatchTypeClass = new Class[] { aDataHatch, GT_MetaTileEntity_Hatch_Dynamo.class, GT_MetaTileEntity_Hatch_Energy.class, GT_MetaTileEntity_Hatch_Input.class, GT_MetaTileEntity_Hatch_InputBus.class, GT_MetaTileEntity_Hatch_Maintenance.class, GT_MetaTileEntity_Hatch_Muffler.class, GT_MetaTileEntity_Hatch_Output.class, GT_MetaTileEntity_Hatch_OutputBus.class, GT_MetaTileEntity_Hatch.class }; } else { aHatchTypeClass = new Class[] { GT_MetaTileEntity_Hatch_Dynamo.class, GT_MetaTileEntity_Hatch_Energy.class, GT_MetaTileEntity_Hatch_Input.class, GT_MetaTileEntity_Hatch_InputBus.class, GT_MetaTileEntity_Hatch_Maintenance.class, GT_MetaTileEntity_Hatch_Muffler.class, GT_MetaTileEntity_Hatch_Output.class, GT_MetaTileEntity_Hatch_OutputBus.class, GT_MetaTileEntity_Hatch.class }; } } } mLayerData[x][z] = new LayerBlockData(aBlock, aMeta, canBeHatch, aHatchTypeClass); return true; } /** * Adds a controller to the layer at the designated location, Details about the controller do not need to be specified. * @param x - The X location, where 0 is the top left corner & counts upwards, moving right. * @param z - The Z location, where 0 is the top left corner & counts upwards, moving down. * @return - Is this controller added to the layer data map? */ public boolean addController(int x, int z) { setControllerLocation(new Pair(x, z)); return addBlockForPos(GregTech_API.sBlockMachines, 0, x, z, true, GT_MetaTileEntity_MultiBlockBase.class); } /** * Adds a Muffler to the layer at the designated location. * @param x - The X location, where 0 is the top left corner & counts upwards, moving right. * @param z - The Z location, where 0 is the top left corner & counts upwards, moving down. * @return - Is this controller added to the layer data map? */ public boolean addMuffler(Block aBlock, int aMeta, int x, int z) { return addBlockForPos(aBlock, aMeta, x, z, true, GT_MetaTileEntity_Hatch_Muffler.class); } /** * Adds a Maint Hatch to the layer at the designated location. * @param x - The X location, where 0 is the top left corner & counts upwards, moving right. * @param z - The Z location, where 0 is the top left corner & counts upwards, moving down. * @return - Is this controller added to the layer data map? */ public boolean addMaintHatch(Block aBlock, int aMeta, int x, int z) { return addBlockForPos(aBlock, aMeta, x, z, true, GT_MetaTileEntity_Hatch_Maintenance.class); } /** * Adds ah Input to the layer at the designated location. * @param x - The X location, where 0 is the top left corner & counts upwards, moving right. * @param z - The Z location, where 0 is the top left corner & counts upwards, moving down. * @return - Is this controller added to the layer data map? */ public boolean addInputBus(Block aBlock, int aMeta, int x, int z) { return addBlockForPos(aBlock, aMeta, x, z, true, GT_MetaTileEntity_Hatch_InputBus.class); } /** * Adds an Output to the layer at the designated location. * @param x - The X location, where 0 is the top left corner & counts upwards, moving right. * @param z - The Z location, where 0 is the top left corner & counts upwards, moving down. * @return - Is this controller added to the layer data map? */ public boolean addOutputBus(Block aBlock, int aMeta, int x, int z) { return addBlockForPos(aBlock, aMeta, x, z, true, GT_MetaTileEntity_Hatch_OutputBus.class); } /** * Adds ah Input to the layer at the designated location. * @param x - The X location, where 0 is the top left corner & counts upwards, moving right. * @param z - The Z location, where 0 is the top left corner & counts upwards, moving down. * @return - Is this controller added to the layer data map? */ public boolean addInputHatch(Block aBlock, int aMeta, int x, int z) { return addBlockForPos(aBlock, aMeta, x, z, true, GT_MetaTileEntity_Hatch_Input.class); } /** * Adds an Output to the layer at the designated location. * @param x - The X location, where 0 is the top left corner & counts upwards, moving right. * @param z - The Z location, where 0 is the top left corner & counts upwards, moving down. * @return - Is this controller added to the layer data map? */ public boolean addOutputHatch(Block aBlock, int aMeta, int x, int z) { return addBlockForPos(aBlock, aMeta, x, z, true, GT_MetaTileEntity_Hatch_Output.class); } /** * Adds ah Input to the layer at the designated location. * @param x - The X location, where 0 is the top left corner & counts upwards, moving right. * @param z - The Z location, where 0 is the top left corner & counts upwards, moving down. * @return - Is this controller added to the layer data map? */ public boolean addInput(Block aBlock, int aMeta, int x, int z) { return addBlockForPos(aBlock, aMeta, x, z, true, new Class[] {GT_MetaTileEntity_Hatch_Input.class, GT_MetaTileEntity_Hatch_InputBus.class}); } /** * Adds an Output to the layer at the designated location. * @param x - The X location, where 0 is the top left corner & counts upwards, moving right. * @param z - The Z location, where 0 is the top left corner & counts upwards, moving down. * @return - Is this controller added to the layer data map? */ public boolean addOutput(Block aBlock, int aMeta, int x, int z) { return addBlockForPos(aBlock, aMeta, x, z, true, new Class[] {GT_MetaTileEntity_Hatch_Output.class, GT_MetaTileEntity_Hatch_OutputBus.class}); } /** * Adds ah Input to the layer at the designated location. * @param x - The X location, where 0 is the top left corner & counts upwards, moving right. * @param z - The Z location, where 0 is the top left corner & counts upwards, moving down. * @return - Is this controller added to the layer data map? */ public boolean addEnergyInput(Block aBlock, int aMeta, int x, int z) { return addBlockForPos(aBlock, aMeta, x, z, true, GT_MetaTileEntity_Hatch_Energy.class); } /** * Adds an Output to the layer at the designated location. * @param x - The X location, where 0 is the top left corner & counts upwards, moving right. * @param z - The Z location, where 0 is the top left corner & counts upwards, moving down. * @return - Is this controller added to the layer data map? */ public boolean addEnergyOutput(Block aBlock, int aMeta, int x, int z) { return addBlockForPos(aBlock, aMeta, x, z, true, GT_MetaTileEntity_Hatch_Dynamo.class); } /** * * @param aBlock - The block you expect. * @param aMeta - The meta you expect. * @param x - the non-relative x location you expect it. * @param z - the non-relative z location you expect it. * @param aDir - The direction the controller is facing. * @return - True if the correct Block was found. May also return true if a hatch is found when allowed or it's the controller. */ public boolean getBlockForPos(Block aBlock, int aMeta, int x, int z, ForgeDirection aDir) { //Logger.INFO("Grid Index X: "+x+" | Z: "+z + " | "+aDir.name()); LayerBlockData g; if (aDir == ForgeDirection.SOUTH) { g = mVariantOrientations.get(2)[x][z]; } else if (aDir == ForgeDirection.WEST) { g = mVariantOrientations.get(3)[x][z]; } else if (aDir == ForgeDirection.NORTH) { LayerBlockData[][] aData = mVariantOrientations.get(0); if (aData != null) { //Logger.INFO("Found Valid Orientation Data. "+aData.length + ", "+aData[0].length); g = aData[x][z]; } else { //Logger.INFO("Did not find valid orientation data."); g = null; } } else if (aDir == ForgeDirection.EAST) { g = mVariantOrientations.get(1)[x][z]; } else { g = mLayerData[x][z]; } if (g == null) { Logger.INFO("Failed to find LayerBlockData. Using AIR_FALLBACK"); //return false;*/ g = LayerBlockData.FALLBACK_AIR_CHECK; } return g.match(aBlock, aMeta); } /** * Is this layer final? * @return - If true, layer data cannot be edited. */ public final boolean isLocked() { return mFinalised; } /** * Used to finalize the layer, after which all four Orientations are then generated. * Cannot be set to false, useful for not locking the layer if an error occurs. * @param lockData */ public final void lock(boolean lockData) { if (!lockData) { Logger.INFO("Failed to lock layer"); return; } //Logger.INFO("Trying to lock layer"); this.mFinalised = true; generateOrientations(); //Logger.INFO("Trying to Build Blueprint Layer [Constructed orietations & finalized]"); } private void generateOrientations() { try { //Logger.INFO("Trying to gen orients for layer"); //North mVariantOrientations.put(mLayerData); LayerBlockData[][] val; //Logger.INFO("1 done"); //East val = rotateArrayClockwise(mLayerData); mVariantOrientations.put((LayerBlockData[][]) val); //Logger.INFO("2 done"); //South val = rotateArrayClockwise(mLayerData); mVariantOrientations.put((LayerBlockData[][]) val); //Logger.INFO("3 done"); //West val = rotateArrayClockwise(mLayerData); mVariantOrientations.put((LayerBlockData[][]) val); //Logger.INFO("4 done"); } catch (Throwable t) { t.printStackTrace(); } } public static LayerBlockData[][] rotateArrayClockwise(LayerBlockData[][] mat) { //Logger.INFO("Rotating Layer 90' Clockwise"); try { final int M = mat.length; final int N = mat[0].length; //Logger.INFO("Dimension X: "+M); //Logger.INFO("Dimension Z: "+N); LayerBlockData[][] ret = new LayerBlockData[N][M]; for (int r = 0; r < M; r++) { for (int c = 0; c < N; c++) { ret[c][M-1-r] = mat[r][c]; } } //Logger.INFO("Returning Rotated Layer"); return ret; } catch (Throwable t) { t.printStackTrace(); return null; } } public boolean hasController() { if (getControllerLocation() == null) { return false; } return true; } public Pair getControllerLocation() { return mControllerLocation; } public void setControllerLocation(Pair mControllerLocation) { if (hasController()) { return; } this.mControllerLocation = mControllerLocation; } public LayerBlockData getDataFromCoordsWithDirection(ForgeDirection aDir, int W, int D) { LayerBlockData g; if (aDir == ForgeDirection.SOUTH) { g = this.mVariantOrientations.get(2)[W][D]; } else if (aDir == ForgeDirection.WEST) { g = this.mVariantOrientations.get(3)[W][D]; } else if (aDir == ForgeDirection.NORTH) { g = this.mVariantOrientations.get(0)[W][D]; } else if (aDir == ForgeDirection.EAST) { g = this.mVariantOrientations.get(1)[W][D]; } else { g = this.mLayerData[W][D]; } return g; } public static Pair rotateOffsetValues(ForgeDirection aDir, int X, int Z) { int offsetX, offsetZ; if (aDir == ForgeDirection.NORTH) { offsetX = X; offsetZ = Z; } else if (aDir == ForgeDirection.EAST) { offsetX = -X; offsetZ = Z; } else if (aDir == ForgeDirection.SOUTH) { offsetX = -X; offsetZ = -Z; } else if (aDir == ForgeDirection.WEST) { offsetX = X; offsetZ = -Z; } else { offsetX = X; offsetZ = Z; } return new Pair(offsetX, offsetZ); } /** * Generates a complete {@link MultiblockLayer} from String data. * @param aDataMap - A {@link HashMap} containing single character {@link String}s, which map to {@link Pair}<{@link Block}, {@link Integer}>s contains pairs of Blocks & Meta. * @param aHorizontalStringRows - The horizontal rows used to map blocks to a grid. Each array slot is one vertical row going downwards as the index increases. * @return */ public static MultiblockLayer generateLayerFromData(HashMap> aDataMap, String[] aHorizontalStringRows) { AutoMap>> x = new AutoMap>>(); for (String u : aDataMap.keySet()) { Pair r = aDataMap.get(u); if (r != null) { x.put(new Pair>(u, r)); } } //String aFreeLetters = "abdefgijklmnopqrstuvwxyz"; /*for (Pair h : aDataMap.values()) { String y = aFreeLetters.substring(0, 0); aFreeLetters = aFreeLetters.replace(y.toLowerCase(), ""); Pair> t = new Pair>(y, h); x.put(t); }*/ return generateLayerFromData(x, aHorizontalStringRows); } /** * Generates a complete {@link MultiblockLayer} from String data. * @param aDataMap - An {@link AutoMap} which contains {@link Pair}s. These Pairs hold a single character {@link String} and another Pair. This inner pair holds a {@link Block} and an {@link Integer}. * @param aHorizontalStringRows - An array which holds the horizontal (X/Width) string data for the layer. * @return A complete Multiblock Layer. */ public static MultiblockLayer generateLayerFromData(AutoMap>> aDataMap, String[] aHorizontalStringRows) { int width = aHorizontalStringRows[0].length(); int depth = aHorizontalStringRows.length; MultiblockLayer L = new MultiblockLayer(width, depth); HashMap> K = new HashMap>(); //24 Free Letters //C = Controller //H = Hatch String aFreeLetters = "abdefgijklmnopqrstuvwxyz"; AutoMap>> j = new AutoMap>>(); //Map the keys to a Hashmap for (Pair> t : aDataMap) { String aKeyTemp = t.getKey(); if (aKeyTemp.toUpperCase().equals("C")){ j.put(t); } else if (aKeyTemp.toUpperCase().equals("H")){ j.put(t); } else { K.put(aKeyTemp.toLowerCase(), t.getValue()); aFreeLetters.replace(aKeyTemp.toLowerCase(), ""); } } //Map any Invalid Characters to new ones, in case someone uses C/H. if (j.size() > 0) { for (Pair> h : j) { String newKey = aFreeLetters.substring(0, 0); K.put(newKey.toLowerCase(), h.getValue()); aFreeLetters.replace(newKey.toLowerCase(), ""); } } int xPos = 0; int zPos = 0; //Vertical Iterator for (String s : aHorizontalStringRows) { //Horizontal Iterator for (int q = 0; q < s.length(); q++) { //Get char as a String at index q. String c = s.substring(q, q); //if the character at c matches the character in this row, we add it to the map. if (c.toLowerCase().equals(s.toLowerCase())) { Pair p = K.get(c); if (c.toLowerCase().equals("c")) { L.addController(xPos, zPos); } else if (c.toLowerCase().equals("h")) { L.addBlockForPos(p.getKey(), p.getValue(), xPos, zPos, true); } else { L.addBlockForPos(p.getKey(), p.getValue(), xPos, zPos, false); } } xPos++; } xPos = 0; zPos++; } L.lock(true); return L; } public static class LayerBlockData{ public static final LayerBlockData FALLBACK_AIR_CHECK = new LayerBlockData(Blocks.air, 0, false); public final Block mBlock; public final int mMeta; public final boolean canBeHatch; public final Class[] mHatchClass; public final boolean isController; public LayerBlockData(Block aBlock, int aMeta, boolean aHatch) { this(aBlock, aMeta, aHatch, new Class[] {}); } public LayerBlockData(Block aBlock, int aMeta, boolean aHatch, Class clazz) { this(aBlock, aMeta, aHatch, new Class[] {clazz}); } public LayerBlockData(Block aBlock, int aMeta, boolean aHatch, Class[] clazz) { mBlock = aBlock; mMeta = aMeta; canBeHatch = aHatch; mHatchClass = clazz; if (clazz != null && clazz.length > 0 && clazz[0].equals(GT_MetaTileEntity_MultiBlockBase.class)) { isController = true; } else { isController = false; } } public boolean match(Block blockToTest, int metaToTest) { //If Both are some kind of Air Block, good enough. if (blockToTest instanceof BlockAir && mBlock instanceof BlockAir) { return true; } if (isController && blockToTest == GregTech_API.sBlockMachines) { return true; } if (canBeHatch && blockToTest == GregTech_API.sBlockMachines) { return true; } if (blockToTest == mBlock && metaToTest == mMeta) { return true; } return false; } } }