/** Runes of Wizardry Mod for Minecraft * Licensed under the GNU GPL version 3 * * this file was created by Xilef11 on 2016-01-06 */ package com.zpig333.runesofwizardry.core.rune; import java.io.BufferedReader; import java.io.BufferedWriter; import java.io.File; import java.io.FileNotFoundException; import java.io.FileReader; import java.io.FileWriter; import java.io.IOException; import java.io.InputStream; import java.io.InputStreamReader; import java.io.Reader; import java.io.Writer; import net.minecraft.item.ItemStack; import net.minecraft.util.EnumFacing; import net.minecraft.util.ResourceLocation; import com.google.gson.Gson; import com.google.gson.JsonIOException; import com.google.gson.JsonSyntaxException; import com.zpig333.runesofwizardry.api.DustRegistry; import com.zpig333.runesofwizardry.api.IDust; import com.zpig333.runesofwizardry.core.References; import com.zpig333.runesofwizardry.tileentity.TileEntityDustPlaced; import com.zpig333.runesofwizardry.util.ArrayUtils; import com.zpig333.runesofwizardry.util.json.JsonUtils; /** This class contains utility methods for managing the ItemStack[][] patterns * @author Xilef11 * */ public class PatternUtils { /** Rotates a given pattern so the top is the facing direction, * i.e if the top row contains the northmost elements, * makes it so that the top row contains the [facing]most elements. * @param patternIn an ItemStack[][] where the top row ([0][x]) represents NORTH * @param facing the facing to rotate the pattern to * @return a new ItemStack[][] which is a rotation of {@code patternIn} so that the top row ([0][x]) represents {@code facing} * @throws IllegalArgumentException if {@code facing} is not horizontal (NESW) **/ public static ItemStack[][] rotateToFacing(ItemStack[][] patternIn, EnumFacing facing){ ItemStack[][] result; switch(facing){ case NORTH: result = patternIn; break;//no need to do anything case WEST: result = ArrayUtils.rotateCW(patternIn); break; case SOUTH: result = ArrayUtils.rotate180(patternIn); break; case EAST: result = ArrayUtils.rotateCCW(patternIn); break; default: throw new IllegalArgumentException("Facing: "+facing+" is not horizontal!"); } return result; } /** Rotates a pattern so that what was at the top (north) is now in the provided direction. * i.e if the facing is EAST, the east column will contain the "top" row of the pattern. * This effectively reverses {@link #rotateToFacing(ItemStack[][], EnumFacing)} * @param patternIn the pattern to rotate * @param facing the direction in which to rotate the pattern * @return a copy of {@code patternIn}, rotated in the {@code facing} direction */ public static ItemStack[][] rotateAgainstFacing(ItemStack[][] patternIn, EnumFacing facing){ ItemStack[][] result; switch(facing){ case NORTH: result = patternIn; break;//no need to do anything case WEST: result = ArrayUtils.rotateCCW(patternIn); break; case SOUTH: result = ArrayUtils.rotate180(patternIn); break; case EAST: result = ArrayUtils.rotateCW(patternIn); break; default: throw new IllegalArgumentException("Facing: "+facing+" is not horizontal!"); } return result; } /** Checks if two ItemStack[][] patterns of IDust are equal (for rune purposes) * * @param first the first pattern to compare * @param second the second pattern to compare * @return true if the patterns match, false otherwise (including if any of the ItemStacks do not contain IDusts) */ public static boolean patternsEqual(ItemStack[][] first, ItemStack[][] second){ if(first==second)return true;//for efficiency, but should not happen. //check the size of the patterns to make sure they match if(first.length!=second.length)return false; if(first[0].length!=second[0].length)return false; //check all dust sets in the pattern for(int r=0;r<first.length;r++){ for(int c=0;c<first[0].length;c++){ ItemStack secStack = second[r][c]; ItemStack firstStack = first[r][c]; if(firstStack!=secStack && !(firstStack.isEmpty() && secStack.isEmpty())){//efficiency again //if one is null, its not equal to the other (because empty==empty above) if(firstStack.isEmpty() || secStack.isEmpty())return false; IDust dust1 = DustRegistry.getDustFromItemStack(firstStack); IDust dust2 = DustRegistry.getDustFromItemStack(secStack); //if at least one of the dusts accepts the other as a match, its OK. if(!(dust1.dustsMatch(firstStack, secStack) ||dust2.dustsMatch(secStack, firstStack))){ return false; } } } } return true; } /** * Checks if a pattern is empty * @param pattern the pattern to check * @return true if all ItemStacks are null */ public static boolean isEmpty(ItemStack[][] pattern){ for(ItemStack[] i:pattern){ for(ItemStack s:i){ if(!s.isEmpty())return false; } } return true; } /** * Writes an ItemStack[][] pattern to a JSON file, which is <MC run dir>/runesofwizardry_exported_patterns/<name>.json. * <br/> Note that if a file with that name already exists, it is saved as <name>_n.json, i.e example_2.json * @param pattern The pattern to save * @param name the name of the file to write * @return the File object representing the written JSON file * @throws JsonIOException * @throws IOException */ public static File exportPatternJson(ItemStack[][] pattern, String name) throws JsonIOException, IOException{ File exportFolder = new File(References.export_folder); exportFolder.mkdir(); Gson gson = JsonUtils.getItemStackGson(); if(!(name.endsWith(".json")||name.endsWith(".JSON")))name+=".json"; File file = new File(exportFolder,name); //add file number if it exists int n=2; while(!file.createNewFile()){ file = new File(exportFolder, name+"_"+n+".json"); } Writer out = new BufferedWriter(new FileWriter(file)); //Convert ItemStack.EMPTY to null (makes things easier on import to make sure == works) for(int i=0;i<pattern.length;i++){ for(int j=0;j<pattern[i].length;j++){ if(pattern[i][j].isEmpty())pattern[i][j]=null; } } gson.toJson(pattern,out); out.close(); return file; } /** * Returns the ItemStack[][] pattern described by the json file at a ResourceLocation<br/> * Note that the JSON file must have been created by PatternUtils#exportPatternJson (i.e the rw_export command) * @param location the ResourceLocation of the json file, i.e: modid:patterns/pattern.json would point to assets/modid/patterns/pattern.json inside your jar * @return the ItemStack[][] pattern described by the file at <location> * @throws IOException * @throws JsonSyntaxException * @throws JsonIOException */ public static ItemStack[][] importFromJson(ResourceLocation location) throws IOException, JsonSyntaxException, JsonIOException{ String path = "assets/"+location.getResourceDomain()+"/"+location.getResourcePath(); //Supposedly the "normal" way to do it, but always return null //InputStream in = Loader.instance().getModClassLoader().getResourceAsStream(path); //This works (with no initial / only). (seems to work even if not our jar) InputStream in = PatternUtils.class.getClassLoader().getResourceAsStream(path); if(in==null)throw new FileNotFoundException("Could not find file: "+path); Gson gson = JsonUtils.getItemStackGson(); Reader read = new BufferedReader(new InputStreamReader(in)); ItemStack[][] pattern = gson.fromJson(read, ItemStack[][].class); read.close(); //convert nulls to ItemStack.EMPTY for(int i=0;i<pattern.length;i++){ for(int j=0;j<pattern[i].length;j++){ if(pattern[i][j]==null)pattern[i][j]=ItemStack.EMPTY; } } return pattern; } /** * Returns the ItemStack[][] pattern described by the json file {@code filename} in the export dir (runesofwizardry_patterns) * Note that the JSON file must have been created by PatternUtils#exportPatternJson (i.e the rw_export command) * If the given String is of the form modid:path, this will wrap it in a ResourceLocation and call {@link #importFromJson(ResourceLocation)} * @param filename the name of the file to import. .json will be appended to it if it isn't already * @return the ItemStack[][] pattern described by the file supplied * @throws IOException If the file can't be closed after reading * @throws FileNotFoundException if the supplied filename can't be found */ public static ItemStack[][] importFromJson(String filename)throws IOException,FileNotFoundException{ if(filename.contains(":"))return importFromJson(new ResourceLocation(filename)); File infile = new File(References.export_folder,filename); if(!infile.exists()){ infile = new File(References.export_folder,filename+".json"); if(!infile.exists()){ infile = new File(References.export_folder,filename+".JSON"); if(!infile.exists()){ throw new FileNotFoundException("Could not find "+filename+", "+filename+".json, or "+filename+".JSON"); } } } Reader read = new FileReader(infile); Gson gson = JsonUtils.getItemStackGson(); ItemStack[][] stacks = gson.fromJson(read, ItemStack[][].class); //convert nulls to ItemStack.EMPTY for(int i=0;i<stacks.length;i++){ for(int j=0;j<stacks[i].length;j++){ if(stacks[i][j]==null)stacks[i][j]=ItemStack.EMPTY; } } read.close(); return stacks; } /** * Converts an ItemStack pattern to an array of TileEntityDustPlaced's contents that form the pattern. * It should be assumed that the top-left contents is the north-west corner. * @param pattern the pattern to convert * @return the contents of the TileEntityDustPlaced required to form the pattern */ public static ItemStack[][][][] toContentsArray(ItemStack[][] pattern){ int dustrows = pattern.length, dustcols = pattern[0].length; int rows = dustrows/TileEntityDustPlaced.ROWS, cols = dustcols/TileEntityDustPlaced.COLS; ItemStack[][][][] result = new ItemStack[rows][cols][TileEntityDustPlaced.ROWS][TileEntityDustPlaced.COLS]; for(int r=0;r<dustrows;r++){ for(int c=0;c<dustcols;c++){ result[r/TileEntityDustPlaced.ROWS][c/TileEntityDustPlaced.COLS][r%TileEntityDustPlaced.ROWS][c%TileEntityDustPlaced.COLS]=pattern[r][c]; } } return result; } }