/** Runes of Wizardry Mod for Minecraft
* Licensed under the GNU GPL version 3
*
* this file was created by Xilef11 on 2015-12-14
*/
package com.zpig333.runesofwizardry.core.rune;
import java.util.HashMap;
import java.util.Map;
import java.util.Set;
import net.minecraft.item.ItemStack;
import net.minecraft.tileentity.TileEntity;
import net.minecraft.util.EnumFacing;
import net.minecraft.util.math.BlockPos;
import net.minecraft.world.World;
import com.zpig333.runesofwizardry.core.WizardryLogger;
import com.zpig333.runesofwizardry.tileentity.TileEntityDustPlaced;
/**
* This class Finds a pattern of dust around a block in the World.
* @author Xilef11
*
*/
public class PatternFinder {
//easy reference to the World
private World world;
private DustElement initial;
private BlockPos northMost,
eastMost,
southMost,
westMost;
private Map<BlockPos, DustElement> map;
public PatternFinder(World world,BlockPos initialBlock){
this.world=world;
initial = new DustElement(initialBlock);
map = new HashMap<BlockPos, DustElement>();
map.put(initialBlock, initial);
northMost=eastMost=westMost=southMost=initialBlock;
}
/**
* Launches the search for a dust pattern
*/
public void search(){
try{
recursiveSearch(initial);
}catch(StackOverflowError e){
WizardryLogger.logError("Stack Overflowed");
}
WizardryLogger.logInfo("Finished finding dust pattern. Found "+map.size()+" blocks in "+n+" calls");
}
//actually creates the structure for dusts
private int n=0;
private void recursiveSearch(DustElement current){
n++;
if(current.isInactiveDusts()){
//set NESWmost
BlockPos pos = current.getPos();
//north is negative Z
if(pos.getZ()<northMost.getZ())northMost=pos;
if(pos.getZ()>southMost.getZ())southMost=pos;
//EAST is X+
if(pos.getX()>eastMost.getX())eastMost=pos;
if(pos.getX()<westMost.getX())westMost=pos;
}
//find all neighbouring blocks
for(EnumFacing face:EnumFacing.HORIZONTALS){
if(current.getNeighbour(face)==null){//if we don't already have a neighbour in that direction
//get the neighbour in the appropriate direction
BlockPos next = current.pos.offset(face);
DustElement neigh = new DustElement(next);
/*this means some blocks (corners between two "live" blocks) will get checked twice,
* but ensures we will detect all dusts correctly (and also cleans up the map)
*/
if(neigh.isInactiveDusts())map.put(next, neigh);
//max of 1 block gap for the search.
if(current.isInactiveDusts()||neigh.isInactiveDusts()){
recursiveSearch(neigh);
}
}
}
}
public ItemStack[][] toArray(){
BlockPos nwCorner = getNW();
BlockPos seCorner = getSE();
//horizontal number of blocks
int blocksX = (seCorner.getX() - nwCorner.getX())+1;
//vertical number of blocks
int blocksZ = (seCorner.getZ() - nwCorner.getZ())+1;
WizardryLogger.logInfo("Converting to array: there are "+blocksX+" horizontal blocks and "+blocksZ+" vertical blocks. NW corner is "+nwCorner+" and SE corner is "+seCorner);
ItemStack[][] result = new ItemStack[blocksZ*TileEntityDustPlaced.ROWS][blocksX*TileEntityDustPlaced.COLS];
for(int i=0;i<blocksZ;i++){//for each row of blocks
for(int j=0;j<blocksX;j++){//for each column of blocks
BlockPos currentPos = nwCorner.east(j).south(i);
DustElement elem = map.get(currentPos);
for(int r=0;r<TileEntityDustPlaced.ROWS;r++){//each row in the contents
for(int c=0;c<TileEntityDustPlaced.COLS;c++){//each column in the contents
int row = i*TileEntityDustPlaced.ROWS + r;
int col = j*TileEntityDustPlaced.COLS +c;
//put what dust we found, or nothing if there is nothing
result[row][col] = elem!=null? elem.dusts[r][c] : ItemStack.EMPTY;
}
}
}
}
return result;
}
/**returns the north-west corner of the detected pattern**/
public BlockPos getNW(){
return new BlockPos(westMost.getX(), westMost.getY(),northMost.getZ());
}
/**returns the South-East corner of the detected pattern**/
public BlockPos getSE(){
return new BlockPos(eastMost.getX(), westMost.getY(),southMost.getZ());
}
/**returns the South-West corner of the detected pattern**/
public BlockPos getSW(){
return new BlockPos(westMost.getX(), westMost.getY(),southMost.getZ());
}
/**returns the North-East corner of the detected pattern**/
public BlockPos getNE(){
return new BlockPos(eastMost.getX(), westMost.getY(),northMost.getZ());
}
/** returns the number of dust blocks found**/
public int getNumBlocks(){
return map.size();
}
/**returns the positions of the found blocks of dust**/
public Set<BlockPos> getDustPositions(){
return map.keySet();
}
/**
* Represents an element in the doubly linked structure that finds dust patterns
* @author Xilef11
*
*/
private class DustElement{
//the position of this element
private final BlockPos pos;
//contents of the TileEntityDustPlaced
private final ItemStack[][] dusts;
private final boolean active;
private DustElement(BlockPos pos){
this.pos=pos;
TileEntity ent = world.getTileEntity(pos);
if(ent instanceof TileEntityDustPlaced){
TileEntityDustPlaced ted = (TileEntityDustPlaced)ent;
//XXX this if might not be necessary, because the TE will be removed if it's empty. remove it if performance problems
dusts= !ted.isEmpty()? ted.getContents() : null;
active = ted.isInRune();
}else{
dusts=null;
active=false;
}
}
/**
* Returns true if the block described by this element is Dust
* @return
*/
protected boolean isInactiveDusts(){
return dusts!=null && !active;
}
protected BlockPos getPos(){
return pos;
}
protected DustElement getNeighbour(EnumFacing face){
return map.get(pos.offset(face));
}
}
}