private void print(int x, int y, int z) {
    ReikaJavaLibrary.pConsole(
        "================================================================================================");

    for (int n = 0; n <= ANCHORS; n++) {

      world.clear();
      for (int i = 0; i < MAX_SIZE_X; i++) {
        for (int k = 0; k < MAX_SIZE_Z; k++) {
          Point pt = new Point(i, k);
          if (!anchorRadii.containsKey(pt)) {
            MazePiece p = new MazePiece(this, partSize, pt);
            for (ForgeDirection dir : dirs) {
              if (locationCache[n].get(pt).contains(dir)) p.connect(dir, true);
            }
            int dx = x + i * partSize;
            int dz = z + k * partSize;

            p.generate(world, dx, y, dz);
          }
        }
      }

      ReikaJavaLibrary.pConsole(
          "------------------------------------------------------------------------------------");
      int r = MAX_SIZE_X * partSize;
      char[][] data = new char[r][r];
      for (int i = 0; i < r; i++) {
        for (int k = 0; k < r; k++) {
          int dx = x + i;
          int dz = z + k;
          BlockKey bk = world.getBlock(dx, y + 1, dz); // +1 to not be floor
          char c = '?';
          if (bk != null) {
            if (bk.blockID == Blocks.air) {
              c = ' ';
            } else if (bk.blockID == ChromaBlocks.STRUCTSHIELD.getBlockInstance()) {
              c = '#';
            }
          }
          data[i][k] = c;
        }
      }
      for (int i = 0; i < r; i++) {
        String s = new String(data[i]);
        ReikaJavaLibrary.pConsole(s);
      }
      ReikaJavaLibrary.pConsole(
          "------------------------------------------------------------------------------------");
    }

    ReikaJavaLibrary.pConsole(
        "================================================================================================");
  }
 private Collection<ForgeDirection> getMovementDirections(
     int x, int y, int z, ForgeDirection last, Random rand) {
   if (rand.nextInt(3) == 0
       && this.canMove(x, y, z, last)
       && !this.hasCellFrom(x, y, z, last)) // bias towards continuing direction
   return ReikaJavaLibrary.makeListFrom(last);
   last = pathCache.getLast().getOpposite();
   int n = rand.nextInt(3) == 0 ? 2 : 1;
   // ReikaJavaLibrary.pConsole("Data at "+x+", "+y+", "+z+"; last is "+last);
   ArrayList<ForgeDirection> li = new ArrayList();
   for (ForgeDirection dir : ForgeDirection.VALID_DIRECTIONS) {
     if (li.size() < n) {
       if (dir != last && this.canMove(x, y, z, dir) && !this.hasCellFrom(x, y, z, dir)) {
         li.add(dir);
         // dir = ForgeDirection.VALID_DIRECTIONS[rand.nextInt(6)];
         // ReikaJavaLibrary.pConsole("Random testing "+idx+" of "+li.size());
       }
     } else {
       break;
     }
   }
   if (li.isEmpty()) {
     // ReikaJavaLibrary.pConsole("Had no paths yet has an unvisited neighbor?!!");
   }
   return li;
 }
 public static void addTracker(PlayerTracker pt) {
   String s = pt.getID();
   if (tags.contains(s)) throw new MisuseException("Duplicate PlayerTracker ID: " + s);
   ReikaJavaLibrary.pConsole("DRAGONAPI: Creating player tracker " + s);
   list.add(pt);
   tags.add(s);
 }
 private static ArrayList<String> getModTrees() {
   ArrayList<String> base = ReikaJavaLibrary.getEnumEntriesWithoutInitializing(ModWoodList.class);
   ArrayList<String> li = new ArrayList();
   for (int i = 0; i < base.size(); i++) {
     StringBuilder sb = new StringBuilder();
     String sg = base.get(i);
     if (sg.startsWith("BOP")) {
       sg = sg.substring(3);
       sb.append("Biomes O Plenty ");
       sb.append(ReikaStringParser.capFirstChar(sg));
     } else if (sg.startsWith("BXL")) {
       sg = sg.substring(3);
       sb.append("ExtraBiomes XL ");
       sb.append(ReikaStringParser.capFirstChar(sg));
     } else if (sg.startsWith("MFR")) {
       sg = sg.substring(3);
       sb.append("MineFactory Reloaded ");
       sb.append(ReikaStringParser.capFirstChar(sg));
     } else if (sg.startsWith("IC2")) {
       sg = sg.substring(3);
       sb.append("IndustrialCraft ");
       sb.append(ReikaStringParser.capFirstChar(sg));
     } else if (sg.startsWith("NATURA")) {
       sg = sg.substring(6);
       sb.append("Natura ");
       sb.append(ReikaStringParser.capFirstChar(sg));
     } else {
       sb.append(ReikaStringParser.capFirstChar(sg));
     }
     li.add(sb.toString());
   }
   return li;
 }
 private ForgeDirection getMovementDirection(
     int x, int y, int z, ForgeDirection last, Random rand) {
   // if (rand.nextInt(3) == 0 && this.canMove(x, y, z, last) && !this.hasCellFrom(x, y, z, last))
   // //bias towards continuing direction
   //	return last;
   last = pathCache.getLast().getOpposite();
   // ReikaJavaLibrary.pConsole("Data at "+x+", "+y+", "+z+"; last is "+last);
   ArrayList<ForgeDirection> li =
       ReikaJavaLibrary.makeListFromArray(ForgeDirection.VALID_DIRECTIONS);
   int idx = rand.nextInt(li.size());
   while (li.get(idx) == last
       || !this.canMove(x, y, z, li.get(idx))
       || this.hasCellFrom(x, y, z, li.get(idx))) {
     li.remove(idx);
     // dir = ForgeDirection.VALID_DIRECTIONS[rand.nextInt(6)];
     if (li.isEmpty()) {
       for (int i = 0; i < 6; i++) {
         ForgeDirection dir = ForgeDirection.VALID_DIRECTIONS[i];
         // ReikaJavaLibrary.pConsole(dir+": LAST="+(dir == last)+"| CANMOVE="+!this.canMove(x, y,
         // z, dir)+"| HAS="+this.hasCellFrom(x, y, z, dir));
       }
       // ReikaJavaLibrary.pConsole("UNVISIT="+this.hasUnvisitedNeighbors(x, y, z));
       // ReikaJavaLibrary.pConsole("Had no paths yet has an unvisited neighbor?!!");
     }
     idx = rand.nextInt(li.size());
     // ReikaJavaLibrary.pConsole("Random testing "+idx+" of "+li.size());
   }
   if (li.isEmpty()) {
     // ReikaJavaLibrary.pConsole("Had no paths yet has an unvisited neighbor?!!");
   }
   return li.get(idx);
 }
 private boolean breakCoil() {
   if (inslot[0] == null) return false;
   if (inslot[0].itemID != ItemRegistry.SPRING.getShiftedID()) return false;
   int dmg = inslot[0].getItemDamage();
   float diff = (float) dmg / 65536 * 0.05F;
   boolean rand = ReikaMathLibrary.doWithChance(diff);
   if (rand) ReikaJavaLibrary.pConsole(dmg, Side.SERVER);
   return rand;
 }
 public static boolean addRender(MachineRegistry m, RotaryTERenderer r) {
   if (!renders.containsValue(r)) {
     renders.put(m, r);
     return true;
   } else {
     MachineRegistry parent = ReikaJavaLibrary.getHashMapKeyByValue(renders, r);
     overrides.put(m, parent);
     return false;
   }
 }
 private void dropSpecialOreBlock(
     World world, int x, int y, int z, int dx, int dy, int dz, SpecialOreBlock id, int meta2) {
   if (this.silkTouch()) {
     this.dropItems(
         world, x, y, z, ReikaJavaLibrary.makeListFrom(id.getSilkTouchVersion(world, dx, dy, dz)));
   } else {
     this.dropItems(world, x, y, z, id.getDrops(world, dx, dy, dz, 0));
   }
   ReikaWorldHelper.setBlock(world, dx, dy, dz, id.getReplacementBlock(world, dx, dy, dz));
 }
  /** Renders the TileEntity for the position. */
  public void renderTileEntityExtractorAt(
      TileEntityExtractor tile, double par2, double par4, double par6, float par8) {
    int var9;

    if (!tile.isInWorld()) var9 = 0;
    else var9 = tile.getBlockMetadata();

    ModelExtractor var14;
    var14 = ExtractorModel;

    this.bindTextureByName("/Reika/RotaryCraft/Textures/TileEntityTex/extractortex.png");

    GL11.glPushMatrix();
    GL11.glEnable(GL12.GL_RESCALE_NORMAL);
    if (tile.isInWorld() && MinecraftForgeClient.getRenderPass() == 1) GL11.glEnable(GL11.GL_BLEND);
    GL11.glColor4f(1.0F, 1.0F, 1.0F, 1.0F);
    GL11.glTranslatef((float) par2, (float) par4 + 2.0F, (float) par6 + 1.0F);
    GL11.glScalef(1.0F, -1.0F, -1.0F);
    GL11.glTranslatef(0.5F, 0.5F, 0.5F);
    int var11 = 0; // used to rotate the model about metadata

    if (tile.isInWorld()) {

      switch (tile.getBlockMetadata()) {
        case 0:
          var11 = 0;
          break;
        case 1:
          var11 = 180;
          break;
        case 2:
          var11 = 270;
          break;
        case 3:
          var11 = 90;
          break;
      }

      if (tile.getBlockMetadata() <= 3) GL11.glRotatef((float) var11 + 90, 0.0F, 1.0F, 0.0F);
      else {
        GL11.glRotatef(var11, 1F, 0F, 0.0F);
        GL11.glTranslatef(0F, -1F, 1F);
        if (tile.getBlockMetadata() == 5) GL11.glTranslatef(0F, 0F, -2F);
      }
    }

    var14.renderAll(
        tile, ReikaJavaLibrary.makeListFrom(MinecraftForgeClient.getRenderPass() == 1), 0, 0);

    if (tile.isInWorld()) GL11.glDisable(GL12.GL_RESCALE_NORMAL);
    GL11.glDisable(GL11.GL_BLEND);
    GL11.glPopMatrix();
    GL11.glColor4f(1.0F, 1.0F, 1.0F, 1.0F);
  }
 public void readFromNBT(NBTTagCompound tag) {
   NBTTagList li = tag.getTagList("locs", NBTTypes.COMPOUND.ID);
   for (Object o : li.tagList) {
     NBTTagCompound data = (NBTTagCompound) o;
     WorldLocation loc = WorldLocation.readFromNBT(data);
     TileEntity te = loc.getTileEntity();
     try {
       V v = (V) te;
       this.data.put(loc, v);
     } catch (ClassCastException e) { // ugly, but no other way to test if te instanceof V
       ReikaJavaLibrary.pConsole("Tried to load a TileEntityCache from invalid NBT!");
     }
   }
 }
 protected void printSafeList() {
   for (int i = 0; i < safePlayers.size(); i++) {
     String player = safePlayers.get(i);
     if (player == null) player = "NULL PLAYER IN SLOT " + i;
     else if (player.isEmpty()) player = "EMPTY STRING PLAYER IN SLOT " + i;
     ReikaJavaLibrary.pConsole(
         "Side "
             + FMLCommonHandler.instance().getEffectiveSide()
             + ": Safe Player "
             + (i + 1)
             + " of "
             + safePlayers.size()
             + ": "
             + player);
   }
 }
 private void dropBlock(
     World world, int x, int y, int z, int dx, int dy, int dz, Block id, int meta2) {
   if (this.silkTouch()) {
     this.dropItems(world, x, y, z, ReikaJavaLibrary.makeListFrom(new ItemStack(id, 1, meta2)));
   } else {
     this.dropItems(world, x, y, z, id.getDrops(world, dx, dy, dz, meta2, 0));
   }
   this.getFillerBlock(world, dx, dy, dz, id, meta2).place(world, dx, dy, dz);
   ReikaSoundHelper.playBreakSound(world, dx, dy, dz, id);
   ReikaPacketHelper.sendDataPacket(
       DragonAPIInit.packetChannel,
       PacketIDs.BREAKPARTICLES.ordinal(),
       world,
       dx,
       dy,
       dz,
       Block.getIdFromBlock(id),
       meta2);
 }
  /** Contains a helper function to avoid overwriting existing aspects. */
  public static void addAspects(ItemStack is, Object... aspects) {
    if (aspects.length % 2 != 0) {
      DragonAPICore.logError(
          "Could not add aspects to " + is + ": You must specify a level for every aspect!");
      ReikaJavaLibrary.dumpStack();
      return;
    }
    AspectList has = ThaumcraftApi.objectTags.get(Arrays.asList(is.getItem(), is.getItemDamage()));
    AspectList ot = getAspectList(aspects);

    if (has != null) {
      for (Aspect as : has.aspects.keySet()) {
        ot.merge(as, has.getAmount(as));
      }
    }
    clearNullAspects(ot);
    ThaumcraftApi.registerObjectTag(is, ot);
    DragonAPICore.log("Registering " + is + " aspects " + aspectsToString(ot));
  }
 private void collectSortAndReplace(Comparator c) {
   ArrayList<CreativeTabs> list =
       ReikaJavaLibrary.makeListFromArray(CreativeTabs.creativeTabArray);
   for (CreativeTabs t : childTabs) {
     list.remove(t);
   }
   Collections.sort(list, c);
   for (CreativeTabs p : tabGroups.keySet()) {
     LinkedList<CreativeTabs> ch = tabGroups.get(p);
     int index = list.indexOf(p) + 1;
     for (CreativeTabs t : ch) {
       list.add(index, t);
       index++;
     }
   }
   CreativeTabs.creativeTabArray = new CreativeTabs[list.size()];
   for (int i = 0; i < list.size(); i++) {
     CreativeTabs t = list.get(i);
     t.tabIndex = i;
     CreativeTabs.creativeTabArray[i] = t;
   }
 }
  public ArrayList<ItemStack> getItemStacks() {
    if (this.isMachine()) return ReikaJavaLibrary.makeListFrom(machine.getCraftedProduct());
    if (this == STORAGE) {
      ArrayList<ItemStack> li = new ArrayList();
      for (int i = 0; i < ChromaItems.STORAGE.getNumberMetadatas(); i++) {
        li.add(ChromaItems.STORAGE.getStackOfMetadata(i));
      }
      return li;
    }
    if (this == GLOW) {
      ArrayList<ItemStack> li = new ArrayList();
      for (int i = 0; i < 48; i++) {
        li.add(ChromaBlocks.GLOW.getStackOfMetadata(i));
      }
      return li;
    }
    if (this == RELAY) {
      ArrayList<ItemStack> li = new ArrayList();
      li.add(ChromaTiles.RELAYSOURCE.getCraftedProduct());
      for (int i = 0; i < 16; i++) {
        li.add(ChromaBlocks.RELAY.getStackOfMetadata(i));
      }
      li.add(ChromaBlocks.RELAY.getStackOfMetadata(16));
      return li;
    }
    if (this == PENDANT) {
      ArrayList<ItemStack> li = new ArrayList();
      for (int i = 0; i < 16; i++) {
        li.add(ChromaItems.PENDANT.getStackOfMetadata(i));
        li.add(ChromaItems.PENDANT3.getStackOfMetadata(i));
      }
      return li;
    }
    if (this == ENDERCRYS) {
      ArrayList<ItemStack> li = new ArrayList();
      li.add(ChromaItems.ENDERCRYSTAL.getStackOfMetadata(0));
      li.add(ChromaItems.ENDERCRYSTAL.getStackOfMetadata(1));
      return li;
    }
    if (this == AUGMENT) {
      ArrayList<ItemStack> li = new ArrayList();
      li.add(ChromaStacks.speedUpgrade);
      li.add(ChromaStacks.efficiencyUpgrade);
      li.add(ChromaStacks.silkUpgrade);
      return li;
    }
    if (item != null) {
      if (item.getItemInstance() instanceof ItemCrystalBasic) {
        ArrayList<ItemStack> li = new ArrayList();
        for (int i = 0; i < 16; i++) {
          li.add(item.getStackOfMetadata(i));
        }
        return li;
      }
      return ReikaJavaLibrary.makeListFrom(item.getStackOf());
    }
    if (iconItem != null && iconItem.getItem() instanceof ItemCrystalBasic) {
      ArrayList<ItemStack> li = new ArrayList();
      for (int i = 0; i < 16; i++) {
        li.add(new ItemStack(iconItem.getItem(), 1, i));
      }
      return li;
    }
    if (this == GROUPS) {
      ArrayList<ItemStack> li = new ArrayList();
      for (int i = 0; i < 13; i++) {
        li.add(ChromaItems.CLUSTER.getStackOfMetadata(i));
      }
      return li;
    }
    if (this == ALLOYS) {
      return new ArrayList(PoolRecipes.instance.getAllOutputItems());
    }
    if (this == ORES) {
      ArrayList<ItemStack> li = new ArrayList();
      li.add(ChromaStacks.chromaDust);
      for (int i = 0; i < 16; i++) li.add(ChromaItems.ELEMENTAL.getStackOfMetadata(i));
      li.add(ChromaStacks.focusDust);
      li.add(ChromaStacks.bindingCrystal);
      li.add(ChromaStacks.enderDust);
      li.add(ChromaStacks.waterDust);
      li.add(ChromaStacks.firaxite);
      li.add(ChromaStacks.spaceDust);
      li.add(ChromaStacks.resocrystal);
      return li;
    }
    if (this == DUSTS) {
      ArrayList<ItemStack> li = new ArrayList();
      li.add(ChromaStacks.auraDust);
      li.add(ChromaStacks.purityDust);
      li.add(ChromaStacks.elementDust);
      li.add(ChromaStacks.beaconDust);
      li.add(ChromaStacks.resonanceDust);
      return li;
    }
    if (this == IRID) {
      ArrayList<ItemStack> li = new ArrayList();
      li.add(ChromaStacks.rawCrystal);
      li.add(ChromaStacks.iridCrystal);
      li.add(ChromaStacks.iridChunk);
      return li;
    }
    if (this == CORES) {
      ArrayList<ItemStack> li = new ArrayList();
      li.add(ChromaStacks.crystalFocus);
      li.add(ChromaStacks.energyCore);
      li.add(ChromaStacks.transformCore);
      li.add(ChromaStacks.voidCore);
      li.add(ChromaStacks.elementUnit);
      li.add(ChromaStacks.crystalLens);
      return li;
    }
    if (this == PATH) {
      ArrayList<ItemStack> li = new ArrayList();
      for (int i = 0; i < ChromaBlocks.PATH.getNumberMetadatas(); i++) {
        li.add(new ItemStack(ChromaBlocks.PATH.getBlockInstance(), 1, i));
      }
      return li;
    }
    if (this == CRYSTALSTONE) {
      ArrayList<ItemStack> li = new ArrayList();
      for (int i = 0; i < ChromaBlocks.PYLONSTRUCT.getNumberMetadatas(); i++) {
        li.add(new ItemStack(ChromaBlocks.PYLONSTRUCT.getBlockInstance(), 1, i));
      }
      return li;
    }
    if (this == BEES) {
      ArrayList<ItemStack> li = new ArrayList();
      World world = ReikaWorldHelper.getBasicReferenceWorld();

      li.add(CrystalBees.getCrystalBee().getBeeItem(world, EnumBeeType.DRONE));
      li.add(CrystalBees.getCrystalBee().getBeeItem(world, EnumBeeType.PRINCESS));
      li.add(CrystalBees.getCrystalBee().getBeeItem(world, EnumBeeType.QUEEN));

      li.add(CrystalBees.getPureBee().getBeeItem(world, EnumBeeType.DRONE));
      li.add(CrystalBees.getPureBee().getBeeItem(world, EnumBeeType.PRINCESS));
      li.add(CrystalBees.getPureBee().getBeeItem(world, EnumBeeType.QUEEN));

      for (int i = 0; i < 16; i++) {
        CrystalElement e = CrystalElement.elements[i];
        li.add(CrystalBees.getElementalBee(e).getBeeItem(world, EnumBeeType.DRONE));
        li.add(CrystalBees.getElementalBee(e).getBeeItem(world, EnumBeeType.PRINCESS));
        li.add(CrystalBees.getElementalBee(e).getBeeItem(world, EnumBeeType.QUEEN));
      }

      return li;
    }
    if (block != null) {
      Item item = Item.getItemFromBlock(block.getBlockInstance());
      ArrayList<ItemStack> li = new ArrayList();
      if (item instanceof ItemBlockDyeTypes
          || item instanceof ItemBlockCrystalColors
          || item instanceof ItemBlockCrystal) {
        for (int i = 0; i < 16; i++) {
          li.add(block.getStackOfMetadata(i));
        }
      } else {
        li.add(block.getStackOf());
      }
      return li;
    }
    if (this == FENCEAUX || this == TNT || this == TANKAUX)
      return ReikaJavaLibrary.makeListFrom(iconItem);
    return null;
  }
 @Override
 public ArrayList<ItemStack> getAllFruits() {
   return ReikaJavaLibrary.makeListFrom(this.getSeed());
 }
 public final void renderAll(boolean a, boolean b, boolean c, boolean d) {
   Object[] arr = {a, b, c, d};
   this.renderAll(ReikaJavaLibrary.makeListFromArray(arr), 0);
 }
 public final void renderAll(boolean a) {
   this.renderAll(ReikaJavaLibrary.makeListFrom(a), 0);
 }
 public ArrayList<MazePath> getPaths() {
   return ReikaJavaLibrary.makeListFromArray(solutions);
 }
public class ShiftMazeGenerator extends DimensionStructureGenerator {

  // store maze[], each place there is a hole, have a door

  private final ArrayList<Point> endPoints = new ArrayList();

  private final HashMap<Point, Point> anchorRadii = new HashMap();
  private final HashSet<Point> anchorClearance = new HashSet();

  private Point exit;
  private Point entry;

  private Point destination;

  private int copyLength;

  private static final int ANCHORS = 6;

  private PathPre[] path = new PathPre[ANCHORS + 1];
  private LinkedList<ForgeDirection>[] pathCache = new LinkedList[ANCHORS + 1];
  private HashSet<Point> coordCache[] = new HashSet[ANCHORS + 1];
  private MultiMap<Point, ForgeDirection>[] locationCache = new MultiMap[ANCHORS + 1];
  private MazePath[] solutions = new MazePath[ANCHORS + 1];

  private Point step;
  private ForgeDirection nextDir;

  private int partSize = 4;

  public static final int MAX_SIZE_X = getWidth();
  public static final int MAX_SIZE_Z = getWidth();

  private final MultiMap<PointDirection, Coordinate> locks = new MultiMap();

  private static final ArrayList<ForgeDirection> dirs =
      ReikaJavaLibrary.makeListFrom(
          ForgeDirection.EAST, ForgeDirection.WEST, ForgeDirection.NORTH, ForgeDirection.SOUTH);

  private static int getWidth() {
    switch (ChromaOptions.getStructureDifficulty()) {
      case 1:
        return 16;
      case 2:
        return 24;
      case 3:
        return 32;
      default:
        return 32;
    }
  }

  @Override
  public void calculate(int x, int z, Random rand) {

    this.pickPoints(rand, ANCHORS);

    posY = 200;

    for (int i = 0; i < ANCHORS + 1; i++) {
      pathCache[i] = new LinkedList();
      coordCache[i] = new HashSet();
      locationCache[i] = new MultiMap(new MultiMap.HashSetFactory());
      destination = i == ANCHORS ? exit : endPoints.get(i);
      this.generatePathFrom(i, entry.x, entry.y, rand);
      this.cutExit(i);
    }

    this.generateBlocks(x, posY, z, rand);
    this.generateAnchors(x, posY, z, rand);
  }

  private void generateAnchors(int x, int y, int z, Random rand) {
    int i = 0;
    for (Point p : endPoints) {
      MazeAnchor a = new MazeAnchor(this, partSize, i, rand);
      int dx = x + p.x * partSize;
      int dz = z + p.y * partSize;
      a.generate(world, dx, y, dz);
      i++;
    }
  }

  private void cutExit(int layer) {
    boolean end = layer == ANCHORS;
    if (end) {
      locationCache[layer].addValue(exit, this.getExitDirectionTo(exit));
    } else {

    }
  }

  private void generateBlocks(int x, int y, int z, Random rand) {
    // s = s+2;
    for (int i = 0; i < MAX_SIZE_X; i++) {
      for (int k = 0; k < MAX_SIZE_Z; k++) {
        Point pt = new Point(i, k);
        if (!anchorRadii.containsKey(pt)) {
          MazePiece p = new MazePiece(this, partSize, pt);
          for (ForgeDirection dir : dirs) {
            int c = this.getConnection(pt, dir);
            if (c > 0) {
              p.connect(dir, c == ANCHORS + 1);
            }
          }
          int dx = x + i * partSize;
          int dz = z + k * partSize;

          p.generate(world, dx, y, dz);
        }
      }
    }

    // if (DragonAPICore.isReikasComputer() && ReikaObfuscationHelper.isDeObfEnvironment()) {
    //	this.print(x, y, z);
    // }
  }

  private void print(int x, int y, int z) {
    ReikaJavaLibrary.pConsole(
        "================================================================================================");

    for (int n = 0; n <= ANCHORS; n++) {

      world.clear();
      for (int i = 0; i < MAX_SIZE_X; i++) {
        for (int k = 0; k < MAX_SIZE_Z; k++) {
          Point pt = new Point(i, k);
          if (!anchorRadii.containsKey(pt)) {
            MazePiece p = new MazePiece(this, partSize, pt);
            for (ForgeDirection dir : dirs) {
              if (locationCache[n].get(pt).contains(dir)) p.connect(dir, true);
            }
            int dx = x + i * partSize;
            int dz = z + k * partSize;

            p.generate(world, dx, y, dz);
          }
        }
      }

      ReikaJavaLibrary.pConsole(
          "------------------------------------------------------------------------------------");
      int r = MAX_SIZE_X * partSize;
      char[][] data = new char[r][r];
      for (int i = 0; i < r; i++) {
        for (int k = 0; k < r; k++) {
          int dx = x + i;
          int dz = z + k;
          BlockKey bk = world.getBlock(dx, y + 1, dz); // +1 to not be floor
          char c = '?';
          if (bk != null) {
            if (bk.blockID == Blocks.air) {
              c = ' ';
            } else if (bk.blockID == ChromaBlocks.STRUCTSHIELD.getBlockInstance()) {
              c = '#';
            }
          }
          data[i][k] = c;
        }
      }
      for (int i = 0; i < r; i++) {
        String s = new String(data[i]);
        ReikaJavaLibrary.pConsole(s);
      }
      ReikaJavaLibrary.pConsole(
          "------------------------------------------------------------------------------------");
    }

    ReikaJavaLibrary.pConsole(
        "================================================================================================");
  }

  private int getConnection(Point pt, ForgeDirection dir) {
    int c = 0;
    for (int i = 0; i < ANCHORS + 1; i++) {
      if (locationCache[i].get(pt).contains(dir)) {
        c++;
      }
    }
    return c;
  }

  private void generatePathFrom(int layer, int x, int z, Random rand) {
    ForgeDirection dir = this.getEntryDirectionFrom(entry);
    pathCache[layer].addLast(dir);
    step = new Point(x, z);
    nextDir = this.getMovementDirection(layer, x, z, rand);
    while (!this.isFull(layer)) {
      this.stepPath(layer, step.x, step.y, rand, nextDir);
    }
    solutions[layer] = new MazePath(path[layer], locationCache[layer]);
  }

  private ForgeDirection getEntryDirectionFrom(Point p) {
    if (p.x == 0) return ForgeDirection.EAST;
    else if (p.x == MAX_SIZE_X - 1) return ForgeDirection.WEST;
    else if (p.y == 0) return ForgeDirection.SOUTH;
    else if (p.y == MAX_SIZE_Z - 1) return ForgeDirection.NORTH;
    else throw new IllegalStateException("No direction from " + p + "!?");
  }

  private ForgeDirection getExitDirectionTo(Point p) {
    if (p.x == 0) return ForgeDirection.WEST;
    else if (p.x == MAX_SIZE_X - 1) return ForgeDirection.EAST;
    else if (p.y == 0) return ForgeDirection.NORTH;
    else if (p.y == MAX_SIZE_Z - 1) return ForgeDirection.SOUTH;
    else throw new IllegalStateException("No direction to " + p + "!?");
  }

  private void stepPath(int layer, int x, int z, Random rand, ForgeDirection dir) {
    Point p = new Point(x, z);
    locationCache[layer].addValue(p, dir.getOpposite());
    coordCache[layer].add(p);
    if (this.isFull(layer)) {
      // ReikaJavaLibrary.pConsole("Have "+coordCache.size()+" points; is full; returning from
      // "+dir+" from "+x+", "+z);
      return;
    } /*
      else if (endPoints.containsKey(p) && endPoints.get(p) == null) {
      	endPoints.put(p, new Path(pathCache));
      	dir = pathCache.removeLast();
      	step.translate(-dir.offsetX, -dir.offsetZ);
      	//ReikaJavaLibrary.pConsole("Have "+coordCache.size()+" points; anchor; stepping backward, opposite of "+dir+", from "+x+", "+z+" to "+step);
      	nextDir = dir.getOpposite();
      }*/ else if (p.equals(destination)) {
      path[layer] = this.getCurrentPath(layer);
      dir = pathCache[layer].removeLast();
      step.translate(-dir.offsetX, -dir.offsetZ);
      // ReikaJavaLibrary.pConsole("Have "+coordCache.size()+" points; destination; stepping
      // backward, opposite of "+dir+", from "+x+", "+z+" to "+step);
      nextDir = dir.getOpposite();
    } else if (anchorRadii.containsKey(p) && anchorRadii.get(p).equals(destination)) {
      Point anch = anchorRadii.get(p);
      for (int a = -1; a <= 1; a++) {
        for (int b = -1; b <= 1; b++) {
          Point dp = new Point(anch.x + a, anch.y + b);
          coordCache[layer].add(dp);
        }
      }
      path[layer] = this.getCurrentPath(layer);
      dir = pathCache[layer].removeLast();
      step.translate(-dir.offsetX, -dir.offsetZ);
      // ReikaJavaLibrary.pConsole("Have "+coordCache.size()+" points; anchor; stepping backward,
      // opposite of "+dir+", from "+x+", "+z+" to "+step);
      nextDir = dir.getOpposite();
    } else if (this.hasUnvisitedNeighbors(layer, x, z)) {
      dir = this.getMovementDirection(layer, x, z, rand);
      locationCache[layer].addValue(p, dir);
      pathCache[layer].addLast(dir);
      // this.stepPath(x+dir.offsetX, y+dir.offsetY, z+dir.offsetZ, rand, dir);
      step.translate(dir.offsetX, dir.offsetZ);
      // ReikaJavaLibrary.pConsole("Have "+coordCache.size()+" points; stepping forward "+dir+" from
      // "+x+", "+z+" to "+step);
      nextDir = dir;
    } else {
      dir = pathCache[layer].removeLast();
      // ReikaJavaLibrary.pConsole("Backstep has: "+coordCache.contains(new
      // Coordinate(x-dir.offsetX, y-dir.offsetY,
      // z-dir.offsetZ))+"|"+this.hasUnvisitedNeighbors(x-dir.offsetX, y-dir.offsetY,
      // z-dir.offsetZ));
      // ReikaJavaLibrary.pConsole("Current has: "+coordCache.contains(new Coordinate(x, y, z)));
      // this.stepPath(x-dir.offsetX, y-dir.offsetY, z-dir.offsetZ, rand, dir);
      step.translate(-dir.offsetX, -dir.offsetZ);
      // ReikaJavaLibrary.pConsole("Have "+coordCache.size()+" points; stepping backward, opposite
      // of "+dir+", from "+x+", "+z+" to "+step);
      nextDir = dir.getOpposite();
      // return;
    }
  }

  private PathPre getCurrentPath(int layer) {
    Point pt = entry;
    LinkedList<Point> li = new LinkedList();
    li.add(pt);
    for (ForgeDirection dir : pathCache[layer]) {
      pt = new Point(pt.x + dir.offsetX, pt.y + dir.offsetZ);
      li.add(pt);
    }
    ArrayList<ForgeDirection> dir = new ArrayList(pathCache[layer]);
    dir.add(ReikaDirectionHelper.getDirectionBetween(li.getLast(), destination));
    return new PathPre(li, dir);
  }

  private boolean isFull(int layer) {
    return coordCache[layer].size() >= MAX_SIZE_X * MAX_SIZE_Z;
  }

  private ForgeDirection getMovementDirection(int layer, int x, int z, Random rand) {
    if (copyLength > 0 || rand.nextInt(4) > 0) {
      ForgeDirection dir = this.getSuccessfulPathDirection(x, z, layer);
      if (dir != null) {
        if (copyLength > 0) {
          copyLength--;
        } else {
          copyLength = 4 + rand.nextInt(9);
          copyLength += 8;
          copyLength *= 5;
        }
        return dir;
      }
    }
    ForgeDirection last = pathCache[layer].getLast().getOpposite();
    ArrayList<ForgeDirection> li = new ArrayList(dirs);
    int idx = rand.nextInt(li.size());
    ForgeDirection side = li.get(idx);
    while (side == last || !this.canMove(x, z, side) || this.hasCellFrom(layer, x, z, side)) {
      li.remove(idx);
      idx = rand.nextInt(li.size());
      side = li.get(idx);
    }
    return li.get(idx);
  }

  private ForgeDirection getSuccessfulPathDirection(int x, int z, int layer) {
    Point pt = new Point(x, z);
    for (int i = 0; i < layer; i++) {
      if (solutions[i].hasPoint(pt)) {
        return solutions[i].getDirection(pt);
      } else {
        int r = 4;
        for (int d = 1; d <= r; d++) {
          for (ForgeDirection dir : dirs) {
            int dx = x + dir.offsetX * d;
            int dz = z + dir.offsetZ * d;
            Point dp = new Point(dx, dz);
            if (solutions[i].hasPoint(dp)
                || dp.equals(destination)
                || (anchorRadii.containsKey(dp) && anchorRadii.get(dp).equals(destination))) {
              return dir;
            }
          }
        }
      }
    }
    return null;
  }

  private boolean canMove(int x, int z, ForgeDirection dir) {
    int dx = x + dir.offsetX;
    int dz = z + dir.offsetZ;
    return dx >= 0 && dx < MAX_SIZE_X && dz >= 0 && dz < MAX_SIZE_Z;
  }

  private boolean hasCellFrom(int layer, int x, int z, ForgeDirection dir) {
    int dx = x + dir.offsetX;
    int dz = z + dir.offsetZ;
    Point p = new Point(dx, dz);
    return coordCache[layer].contains(p) /* || endPoints.get(p) != null*/;
  }

  private boolean hasUnvisitedNeighbors(int layer, int x, int z) {
    for (ForgeDirection dir : dirs) {
      if (this.canMove(x, z, dir) && !this.hasCellFrom(layer, x, z, dir)) return true;
    }
    return false;
  }

  private void pickPoints(Random rand, int n) {
    entry = this.randomEdgeCenter(rand);
    do {
      exit = this.randomEdgeCenter(rand);
    } while (exit.equals(entry));

    for (int i = 0; i < n; i++) {
      Point p = new Point(1 + rand.nextInt(MAX_SIZE_X - 2), 1 + rand.nextInt(MAX_SIZE_Z - 2));
      while (this.anchorOverlaps(p)) {
        p = new Point(1 + rand.nextInt(MAX_SIZE_X - 2), 1 + rand.nextInt(MAX_SIZE_Z - 2));
      }

      this.addAnchor(p);
    }
  }

  private boolean anchorOverlaps(Point p) {
    for (int a = -2; a <= 2; a++) { // intentionally 2, not 1
      for (int b = -2; b <= 2; b++) {
        Point dp = new Point(p.x + a, p.y + b);
        if (anchorClearance.contains(p) || dp.equals(exit) || dp.equals(entry)) return true;
      }
    }
    return false;
  }

  private void addAnchor(Point p) {
    endPoints.add(p);

    // locationCache.addValue(p, ForgeDirection.EAST);
    // locationCache.addValue(p, ForgeDirection.WEST);
    // locationCache.addValue(p, ForgeDirection.SOUTH);
    // locationCache.addValue(p, ForgeDirection.NORTH);

    for (int a = -2; a <= 2; a++) {
      for (int b = -2; b <= 2; b++) {
        Point dp = new Point(p.x + a, p.y + b);
        anchorClearance.add(dp);

        if (Math.abs(a) <= 1 && Math.abs(b) <= 1) anchorRadii.put(dp, p);
      }
    }
  }

  private Point randomEdgeCenter(Random rand) {
    Point p = new Point(MAX_SIZE_X / 2, MAX_SIZE_Z / 2);
    switch (rand.nextInt(4)) {
      case 0:
        p.x = MAX_SIZE_X - 1;
        break;
      case 1:
        p.x = 0;
        break;
      case 2:
        p.y = MAX_SIZE_Z - 1;
        break;
      case 3:
        p.y = 0;
        break;
    }
    return p;
  }

  @Override
  protected int getCenterXOffset() {
    return (partSize * (MAX_SIZE_X + 1)) / 2;
  }

  @Override
  protected int getCenterZOffset() {
    return (partSize * (MAX_SIZE_Z + 1)) / 2;
  }

  @Override
  protected void clearCaches() {
    endPoints.clear();
    anchorRadii.clear();
    anchorClearance.clear();

    entry = null;
    exit = null;
    destination = null;

    copyLength = 0;

    path = new PathPre[ANCHORS + 1];
    pathCache = new LinkedList[ANCHORS + 1];
    coordCache = new HashSet[ANCHORS + 1];
    locationCache = new MultiMap[ANCHORS + 1];
    solutions = new MazePath[ANCHORS + 1];

    step = null;
    nextDir = null;

    locks.clear();
  }

  public ArrayList<MazePath> getPaths() {
    return ReikaJavaLibrary.makeListFromArray(solutions);
  }

  public void cacheLock(Point pt, ForgeDirection dir, int x, int y, int z) {
    locks.addValue(new PointDirection(pt, dir), new Coordinate(x, y, z));
  }

  public Collection<Coordinate> getLocks(int x, int z, ForgeDirection dir) {
    return locks.get(new PointDirection(x, z, dir));
  }

  @Override
  public StructureData createDataStorage() {
    return new ShiftMazeData(this);
  }

  private static class PathPre {

    private final LinkedList<Point> points;
    private final ArrayList<ForgeDirection> dirs;

    private PathPre(LinkedList<Point> path, ArrayList<ForgeDirection> dir) {
      points = path;
      dirs = dir;
    }
  }

  public static class MazePath {

    private final LinkedList<Point> solution;
    private final HashMap<Point, ForgeDirection> directions;
    private final MultiMap<Point, ForgeDirection> connections;

    private MazePath(PathPre path, MultiMap<Point, ForgeDirection> con) {
      solution = path.points;
      connections = con;
      directions = new HashMap();

      int i = 0;
      for (Point p : solution) {
        ForgeDirection dir = path.dirs.get(i);
        directions.put(p, dir);
        i++;
      }
    }

    public boolean hasPoint(Point pt) {
      return directions.containsKey(pt);
    }

    public ForgeDirection getDirection(Point pt) {
      return directions.get(pt);
    }

    public boolean isPositionOpen(int x, int z, ForgeDirection dir) {
      return connections.get(new Point(x, z)).contains(dir);
    }
  }
}
 public List<String> getCopyOfSafePlayerList() {
   return ReikaJavaLibrary.copyList(safePlayers);
 }
 public List<ItemStack> getCrafting() {
   if (!this.isCrafting()) return null;
   if (this == SHAFT) {
     List<ItemStack> li = new ArrayList<ItemStack>();
     for (int i = 0; i < MaterialRegistry.values().length; i++) {
       li.add(MachineRegistry.SHAFT.getCraftedMetadataProduct(i));
     }
     return li;
   }
   if (this == GEARBOX) {
     List<ItemStack> li = new ArrayList<ItemStack>();
     for (int i = 0; i < MaterialRegistry.values().length; i++) {
       li.add(MachineRegistry.GEARBOX.getCraftedMetadataProduct(i));
     }
     return li;
   }
   if (this == COIL) {
     List<ItemStack> li = new ArrayList<ItemStack>();
     ItemStack is = MachineRegistry.ADVANCEDGEARS.getCraftedMetadataProduct(2);
     li.add(is);
     is = is.copy();
     is.stackTagCompound = new NBTTagCompound();
     is.stackTagCompound.setBoolean("bedrock", true);
     li.add(is);
     return li;
   }
   if (this == FLYWHEEL) {
     List<ItemStack> li = new ArrayList<ItemStack>();
     for (int i = 0; i < 4; i++) {
       li.add(MachineRegistry.FLYWHEEL.getCraftedMetadataProduct(i));
     }
     return li;
   }
   if (this == OTHERGEAR) {
     List<ItemStack> li = new ArrayList<ItemStack>();
     li.add(ItemStacks.woodgear);
     li.add(ItemStacks.stonegear);
     li.add(ItemStacks.diamondgear);
     li.add(ItemStacks.bedrockgear);
     return li;
   }
   if (this == OTHERSHAFT) {
     List<ItemStack> li = new ArrayList<ItemStack>();
     li.add(ItemStacks.stonerod);
     li.add(ItemStacks.diamondshaft);
     li.add(ItemStacks.bedrockshaft);
     return li;
   }
   if (this == OTHERGEARUNIT) {
     List<ItemStack> li = new ArrayList<ItemStack>();
     li.add(ItemStacks.wood2x);
     li.add(ItemStacks.wood4x);
     li.add(ItemStacks.wood8x);
     li.add(ItemStacks.wood16x);
     li.add(ItemStacks.stone2x);
     li.add(ItemStacks.stone4x);
     li.add(ItemStacks.stone8x);
     li.add(ItemStacks.stone16x);
     li.add(ItemStacks.diamond2x);
     li.add(ItemStacks.diamond4x);
     li.add(ItemStacks.diamond8x);
     li.add(ItemStacks.diamond16x);
     li.add(ItemStacks.bedrock2x);
     li.add(ItemStacks.bedrock4x);
     li.add(ItemStacks.bedrock8x);
     li.add(ItemStacks.bedrock16x);
     return li;
   }
   if (crafted != null) return ReikaJavaLibrary.makeListFrom(crafted);
   if (machine != null && machine.isPipe())
     return ReikaJavaLibrary.makeListFrom(machine.getCraftedProduct());
   if (this == BEDTOOLS) {
     List<ItemStack> li = new ArrayList<ItemStack>();
     li.add(ItemRegistry.BEDPICK.getStackOf());
     li.add(ItemRegistry.BEDSHOVEL.getStackOf());
     li.add(ItemRegistry.BEDAXE.getStackOf());
     li.add(ItemRegistry.BEDSWORD.getStackOf());
     li.add(ItemRegistry.BEDHOE.getStackOf());
     li.add(ItemRegistry.BEDSHEARS.getStackOf());
     li.add(ItemRegistry.BEDSICKLE.getStackOf());
     return li;
   }
   if (this == BEDARMOR) {
     List<ItemStack> li = new ArrayList<ItemStack>();
     li.add(ItemRegistry.BEDHELM.getStackOf());
     li.add(ItemRegistry.BEDCHEST.getStackOf());
     li.add(ItemRegistry.BEDLEGS.getStackOf());
     li.add(ItemRegistry.BEDBOOTS.getStackOf());
     return li;
   }
   if (this == STEELTOOLS) {
     List<ItemStack> li = new ArrayList<ItemStack>();
     li.add(ItemRegistry.STEELPICK.getStackOf());
     li.add(ItemRegistry.STEELSHOVEL.getStackOf());
     li.add(ItemRegistry.STEELAXE.getStackOf());
     li.add(ItemRegistry.STEELSWORD.getStackOf());
     li.add(ItemRegistry.STEELHOE.getStackOf());
     li.add(ItemRegistry.STEELSHEARS.getStackOf());
     li.add(ItemRegistry.STEELSICKLE.getStackOf());
     return li;
   }
   if (this == STEELARMOR) {
     List<ItemStack> li = new ArrayList<ItemStack>();
     li.add(ItemRegistry.STEELHELMET.getStackOf());
     li.add(ItemRegistry.STEELCHEST.getStackOf());
     li.add(ItemRegistry.STEELLEGS.getStackOf());
     li.add(ItemRegistry.STEELBOOTS.getStackOf());
     return li;
   }
   if (this == SOLAR)
     return ReikaJavaLibrary.makeListFrom(MachineRegistry.SOLARTOWER.getCraftedProduct());
   if (this.getParent() == ENGINEDESC)
     return ReikaJavaLibrary.makeListFrom(EngineType.engineList[offset].getCraftedProduct());
   if (machine == MachineRegistry.ADVANCEDGEARS)
     return ReikaJavaLibrary.makeListFrom(
         MachineRegistry.ADVANCEDGEARS.getCraftedMetadataProduct(offset));
   if (this.getParent() == TRANSDESC || this.isMachine()) {
     if (machine.hasCustomPlacerItem())
       return ReikaJavaLibrary.makeListFrom(machine.getCraftedMetadataProduct(0));
     return ReikaJavaLibrary.makeListFrom(machine.getCraftedProduct());
   }
   if (this == DECOBLOCKS) {
     List<ItemStack> li = new ArrayList<ItemStack>();
     li.add(ItemStacks.steelblock);
     li.add(ItemStacks.anthrablock);
     li.add(ItemStacks.lonsblock);
     return li;
   }
   if (this == SLIDES) {
     List<ItemStack> li = new ArrayList<ItemStack>();
     for (int i = 0; i < ItemRegistry.SLIDE.getNumberMetadatas(); i++)
       li.add(ItemRegistry.SLIDE.getStackOfMetadata(i));
     return li;
   }
   // if (this == BEDINGOT)
   //	return ReikaJavaLibrary.makeListFrom(ItemStacks.bedingot);
   if (this == ALLOYING) {
     ArrayList<ItemStack> is = new ArrayList();
     ArrayList<BlastFurnacePattern> li = RecipesBlastFurnace.getRecipes().getAllAlloyingRecipes();
     for (BlastFurnacePattern p : li) {
       is.add(p.outputItem());
     }
     return is;
   }
   if (this == AMMONIUM) return ReikaJavaLibrary.makeListFrom(ItemStacks.nitrate);
   if (this == SALT) return ReikaJavaLibrary.makeListFrom(ItemStacks.salt);
   if (this == SILVERIODIDE) return ReikaJavaLibrary.makeListFrom(ItemStacks.silveriodide);
   if (this == EXPLOSIVES) return ReikaJavaLibrary.makeListFrom(ItemRegistry.SHELL.getStackOf());
   if (this == MINECART) return ReikaJavaLibrary.makeListFrom(ItemRegistry.MINECART.getStackOf());
   return ReikaJavaLibrary.makeListFrom(ItemStacks.basepanel);
 }